From e13a74213c0f51f75c89caaaffc6655bd515a02c Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 6 Nov 2017 17:29:19 -0800 Subject: [PATCH 001/137] this is holder spot to put avatar entity animation code --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 7db19704b4..3959bf0bf0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -979,6 +979,8 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } + //this a spot to work on the avatar npc in my branch + QVector jointsData; const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy From af0304fd6b48122f97373f70c5d751f5c6e5c9e6 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Nov 2017 10:04:12 -0800 Subject: [PATCH 002/137] preliminary changes for the property fixes --- .../src/RenderableModelEntityItem.cpp | 38 +++++++++++++++++-- .../src/RenderableModelEntityItem.h | 2 + libraries/entities/src/ModelEntityItem.cpp | 7 ++++ libraries/entities/src/ModelEntityItem.h | 1 + 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3959bf0bf0..40e5066e2d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -979,6 +979,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } + //added by angus //this a spot to work on the avatar npc in my branch QVector jointsData; @@ -989,6 +990,19 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } + //get entity model anim props + bool isLooping = entity->getAnimationLoop(); + int firstFrame = entity->getAnimationFirstFrame(); + int lastFrame = entity->getAnimationLastFrame(); + bool isHolding = entity->getAnimationHold(); + int updatedFrameCount = frameCount; + + if ((firstFrame >= 0) && (firstFrame < lastFrame) && (lastFrame <= frameCount)) { + //length of animation in now determined by first and last frame + updatedFrameCount = lastFrame - firstFrame; + } + + if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; @@ -997,11 +1011,25 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto now = usecTimestampNow(); auto interval = now - _lastAnimated; _lastAnimated = now; - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + //we handle the hold animation property here + //if hold don't advance the current frame. + if (!isHolding) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + } { - int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % frameCount; + //where are we in the currently defined animation segment? + int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % updatedFrameCount; + //this starts us at the offset first frame. + animationCurrentFrame += firstFrame; + + //here we implement the looping animation property + //if we have played through the animation once then we hold on the last frame + if (!isLooping && (_currentFrame > (updatedFrameCount - 1))) { + animationCurrentFrame = updatedFrameCount + firstFrame; + } + if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; } @@ -1314,7 +1342,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (newAnimationProperties != _renderAnimationProperties) { withWriteLock([&] { _renderAnimationProperties = newAnimationProperties; - _currentFrame = _renderAnimationProperties.getCurrentFrame(); + //if (entity->getAnimationHold()) { + // _currentFrame = _renderAnimationProperties.getCurrentFrame(); + //} }); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0272bed575..d33976e356 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -185,6 +185,8 @@ private: bool _animating { false }; uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; + //bool _previousHold{ false }; + float _currentFramePropertyValue{ 0 }; }; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 26f063f7cc..ae73fa684a 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -538,6 +538,13 @@ void ModelEntityItem::setAnimationLoop(bool loop) { }); } +bool ModelEntityItem::getAnimationLoop() const { + return resultWithReadLock([&] { + return _animationProperties.getLoop(); + }); +} + + void ModelEntityItem::setAnimationHold(bool hold) { withWriteLock([&] { _animationProperties.setHold(hold); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2c3ef3aa2d..698197b1a6 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -90,6 +90,7 @@ public: bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); }; void setAnimationLoop(bool loop); + bool getAnimationLoop() const; void setAnimationHold(bool hold); bool getAnimationHold() const; From a482b7c371086cd06cb9b47937fc33524cb6c45b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Nov 2017 17:09:45 +1300 Subject: [PATCH 003/137] Fix up JSDoc tool readme file --- tools/jsdoc/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/jsdoc/README.md b/tools/jsdoc/README.md index c43f95cabe..5cce6bb2a6 100644 --- a/tools/jsdoc/README.md +++ b/tools/jsdoc/README.md @@ -5,9 +5,9 @@ * Install node.js * Install jsdoc via npm. `npm install jsdoc -g` -To generate html documentation for the High Fidelity JavaScript API +To generate html documentation for the High Fidelity JavaScript API: -`cd scripts/jsdoc` -`jsdoc . -c config.json` +* `cd tools/jsdoc` +* `jsdoc . -c config.json` -The out folder should contain index.html +The out folder should contain index.html. From 7bbe5cdf702f9625e964b6c823433a4d77468dd9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Nov 2017 21:53:55 +1300 Subject: [PATCH 004/137] Update JSDoc for Clipboard JavaScript API --- .../scripting/ClipboardScriptingInterface.cpp | 4 +- .../scripting/ClipboardScriptingInterface.h | 41 ++++++++++++------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index f8db061299..c2d2b69883 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -34,7 +34,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const return retVal; } -bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) { +bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float scale) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "exportEntities", Q_RETURN_ARG(bool, retVal), @@ -42,7 +42,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float Q_ARG(float, x), Q_ARG(float, y), Q_ARG(float, z), - Q_ARG(float, s)); + Q_ARG(float, scale)); return retVal; } diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 826732c777..f6cfc87554 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -18,6 +18,8 @@ #include /**jsdoc + * The Clipboard API enables you to export and import entities to and from JSON files. + * * @namespace Clipboard */ class ClipboardScriptingInterface : public QObject { @@ -30,43 +32,54 @@ signals: public: /**jsdoc + * Compute the extents of the contents held in the clipboard. * @function Clipboard.getContentsDimensions - * @return {Vec3} The extents of the contents held in the clipboard. + * @returns {Vec3} The extents of the contents held in the clipboard. */ Q_INVOKABLE glm::vec3 getContentsDimensions(); /**jsdoc - * Compute largest dimension of the extents of the contents held in the clipboard + * Compute the largest dimension of the extents of the contents held in the clipboard. * @function Clipboard.getClipboardContentsLargestDimension - * @return {float} The largest dimension computed. + * @returns {float} The largest dimension computed. */ Q_INVOKABLE float getClipboardContentsLargestDimension(); /**jsdoc - * Import entities from a .json file containing entity data into the clipboard. - * You can generate * a .json file using {Clipboard.exportEntities}. + * Import entities from a JSON file containing entity data into the clipboard. + * You can generate a JSON file using {@link Clipboard.exportEntities}. * @function Clipboard.importEntities - * @param {string} filename Filename of file to import. - * @return {bool} True if the import was succesful, otherwise false. + * @param {string} filename Path and name of file to import. + * @returns {bool} `true` if the import was successful, otherwise `false`. */ Q_INVOKABLE bool importEntities(const QString& filename); /**jsdoc - * Export the entities listed in `entityIDs` to the file `filename` + * Export the entities specified to a JSON file. * @function Clipboard.exportEntities - * @param {string} filename Path to the file to export entities to. + * @param {string} filename Path and name of the file to export entities to. Should have the extension `.json`. * @param {EntityID[]} entityIDs IDs of entities to export. - * @return {bool} True if the export was succesful, otherwise false. + * @returns {bool} `true` if the export was successful, otherwise `false`. */ Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); - Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s); + + /**jsdoc + * Export the entities with centers within a cube to a JSON file. + * @function Clipboard.exportEntities + * @param {string} filename Path and name of the file to export entities to. Should have the extension `.json`. + * @param {float} x X-coordinate of the cube center. + * @param {float} y Y-coordinate of the cube center. + * @param {float} z Z-coordinate of the cube center. + * @param {float} scale Half dimension of the cube. + * @returns {bool} `true` if the export was successful, otherwise `false`. + */ + Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale); /**jsdoc * Paste the contents of the clipboard into the world. * @function Clipboard.pasteEntities - * @param {Vec3} position Position to paste clipboard at. - * @return {EntityID[]} Array of entity IDs for the new entities that were - * created as a result of the paste operation. + * @param {Vec3} position Position to paste the clipboard contents at. + * @returns {EntityID[]} Array of entity IDs for the new entities that were created as a result of the paste operation. */ Q_INVOKABLE QVector pasteEntities(glm::vec3 position); }; From 875b9846a0295ac1e34574d838580c4a93b55b15 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Nov 2017 21:55:03 +1300 Subject: [PATCH 005/137] Remove unused signal --- interface/src/scripting/ClipboardScriptingInterface.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index f6cfc87554..e9d2e0dd3b 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -27,9 +27,6 @@ class ClipboardScriptingInterface : public QObject { public: ClipboardScriptingInterface(); -signals: - void readyToImport(); - public: /**jsdoc * Compute the extents of the contents held in the clipboard. From 036ac8de6d750a23e0dd26ba7e16d01e009c4190 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Nov 2017 21:55:42 +1300 Subject: [PATCH 006/137] Update JSDoc for Menu JavaScript API --- .../src/scripting/MenuScriptingInterface.h | 63 +++++++++---------- .../script-engine/src/MenuItemProperties.cpp | 27 ++++---- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index b9c29ccf08..dd35947900 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -18,15 +18,15 @@ class MenuItemProperties; /**jsdoc - * The `Menu` provides access to the menu that is shown at the top of the window - * shown on a user's desktop and the right click menu that is accessible - * in both Desktop and HMD mode. + * The Menu API provides access to the menu that is displayed at the top of the window + * on a user's desktop and when the "MENU" button is pressed on the tablet. * *

Groupings

- * A `grouping` is a way to group a set of menus and/or menu items together - * so that they can all be set visible or invisible as a group. There are - * 2 available groups: "Advanced" and "Developer" + * A "grouping" provides a way to group a set of menus or menu items together so + * that they can all be set visible or invisible as a group. + * There are two available groups: "Advanced" and "Developer". * These groupings can be toggled in the "Settings" menu. + * If a menu item doesn't belong to a group it is always displayed. * * @namespace Menu */ @@ -55,8 +55,8 @@ public slots: /**jsdoc * Add a new top-level menu. * @function Menu.addMenu - * @param {string} menuName Name that will be shown in the menu. - * @param {string} grouping Name of the grouping to add this menu to. + * @param {string} menuName Name that will be displayed for the menu. + * @param {string} [grouping] Name of the grouping, if any, to add this menu to. */ void addMenu(const QString& menuName, const QString& grouping = QString()); @@ -71,7 +71,7 @@ public slots: * Check whether a top-level menu exists. * @function Menu.menuExists * @param {string} menuName Name of the menu to check for existence. - * @return {bool} `true` if the menu exists, otherwise `false`. + * @returns {bool} `true` if the menu exists, otherwise `false`. */ bool menuExists(const QString& menuName); @@ -80,14 +80,14 @@ public slots: * The line will be placed at the bottom of the menu. * @function Menu.addSeparator * @param {string} menuName Name of the menu to add a separator to. - * @param {string} separatorName Name of the separator that will be shown (but unclickable) below the separator line. + * @param {string} separatorName Name of the separator that will be displayed as the label below the separator line. */ void addSeparator(const QString& menuName, const QString& separatorName); /**jsdoc - * Remove a separator and its label from a menu. + * Remove a separator from a menu. * @function Menu.removeSeparator - * @param {string} menuName Name of the menu to remove a separator from. + * @param {string} menuName Name of the menu to remove the separator from. * @param {string} separatorName Name of the separator to remove. */ void removeSeparator(const QString& menuName, const QString& separatorName); @@ -95,7 +95,7 @@ public slots: /**jsdoc * Add a new menu item to a menu. * @function Menu.addMenuItem - * @param {Menu.MenuItemProperties} properties + * @param {Menu.MenuItemProperties} properties Properties of the menu item to create. */ void addMenuItem(const MenuItemProperties& properties); @@ -104,16 +104,9 @@ public slots: * @function Menu.addMenuItem * @param {string} menuName Name of the menu to add a menu item to. * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. - * @param {string} shortcutKey A shortcut key that can be used to trigger the menu item. + * @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item. */ void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey); - - /**jsdoc - * Add a new menu item to a menu. - * @function Menu.addMenuItem - * @param {string} menuName Name of the menu to add a menu item to. - * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. - */ void addMenuItem(const QString& menuName, const QString& menuitem); /**jsdoc @@ -129,12 +122,12 @@ public slots: * @function Menu.menuItemExists * @param {string} menuName Name of the menu that the menu item is in. * @param {string} menuItem Name of the menu item to check for existence of. - * @return {bool} `true` if the menu item exists, otherwise `false`. + * @returns {bool} `true` if the menu item exists, otherwise `false`. */ bool menuItemExists(const QString& menuName, const QString& menuitem); /** - * Not working, will not document until fixed + * TODO: Not working; don't document until fixed. */ void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString()); @@ -144,7 +137,7 @@ public slots: * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked * @param {string} menuOption The name of the menu item. - * @return `true` if the option is checked, otherwise false. + * @returns {bool} `true` if the option is checked, otherwise false. */ bool isOptionChecked(const QString& menuOption); @@ -157,39 +150,41 @@ public slots: void setIsOptionChecked(const QString& menuOption, bool isChecked); /**jsdoc - * Toggle the status of a checkable menu item. If it is checked, it will be unchecked. - * If it is unchecked, it will be checked. - * @function Menu.setIsOptionChecked - * @param {string} menuOption The name of the menu item to toggle. + * Trigger the menu item as if the user clicked on it. + * @function Menu.triggerOption + * @param {string} menuOption The name of the menu item to trigger. */ void triggerOption(const QString& menuOption); /**jsdoc - * Check whether a menu is enabled. If a menu is disabled it will be greyed out - * and unselectable. + * Check whether a menu is enabled. If a menu is disabled it is grayed out and unusable. * Menus are enabled by default. * @function Menu.isMenuEnabled * @param {string} menuName The name of the menu to check. - * @return {bool} `true` if the menu is enabled, otherwise false. + * @returns {bool} `true` if the menu is enabled, otherwise false. */ bool isMenuEnabled(const QString& menuName); /**jsdoc - * Set a menu to be enabled or disabled. + * Set a menu to be enabled or disabled. If a menu is disabled it is grayed out and unusable. * @function Menu.setMenuEnabled * @param {string} menuName The name of the menu to modify. * @param {bool} isEnabled Whether the menu will be enabled or not. */ void setMenuEnabled(const QString& menuName, bool isEnabled); + /** + * TODO: Not used or useful; will not document until used. + */ void closeInfoView(const QString& path); bool isInfoViewVisible(const QString& path); signals: /**jsdoc - * This is a signal that is emitted when a menu item is clicked. + * Notifies scripts when a menu item is clicked (or triggered by {@link Menu.triggerOption}). * @function Menu.menuItemEvent - * @param {string} menuItem Name of the menu item that was triggered. + * @param {string} menuItem Name of the menu item that was clicked. + * @returns {Signal} */ void menuItemEvent(const QString& menuItem); }; diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index de76dbc3ca..c24e5a7816 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -51,22 +51,23 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt } /**jsdoc - * `MenuItemProperties` is a list of properties that can be passed to Menu.addMenuItem - * to create a new menu item. + * A set of properties that can be passed to {@link Menu.addMenuItem} to create a new menu item. * - * If none of position, beforeItem, afterItem, or grouping are specified, the - * menu item will be placed in the last position. + * If none of `position`, `beforeItem`, `afterItem`, or `grouping` are specified, the menu item will be placed in the last + * position. * * @typedef {Object} Menu.MenuItemProperties - * @property {string} menuName Name of the top-level menu - * @property {string} menuItemName Name of the menu item - * @property {bool} isCheckable Whether the menu item is checkable or not - * @property {bool} isChecked Where the menu item is checked or not - * @property {string} shortcutKey An optional shortcut key to trigger the menu item. - * @property {int} position The position to place the new menu item. `0` is the first menu item. - * @property {string} beforeItem The name of the menu item to place this menu item before. - * @property {string} afterItem The name of the menu item to place this menu item after. - * @property {string} grouping The name of grouping to add this menu item to. + * @property {string} menuName Name of the menu. + * @property {string} menuItemName Name of the menu item. + * @property {bool} [isCheckable=false] Whether or not the menu item is checkable. + * @property {bool} [isChecked=false] Whether or not the menu item is checked. + * @property {bool} [isSeparator=false] Whether or not the menu item is a separator. + * @property {string} [shortcutKey] A shortcut key that triggers the menu item. + * @property {KeyEvent} [shortcutKeyEvent] A {@link KeyEvent} that specifies a key that triggers the menu item. + * @property {int} [position] The position to place the new menu item. `0` is the first menu item. + * @property {string} [beforeItem] The name of the menu item to place this menu item before. + * @property {string} [afterItem] The name of the menu item to place this menu item after. + * @property {string} [grouping] The name of grouping to add this menu item to. */ void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) { properties.menuName = object.property("menuName").toVariant().toString(); From cf5a7531dfb08afc88cc9c793031732f101bee86 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:10:31 +0100 Subject: [PATCH 007/137] Fixed dithered PCF filter in shadow --- libraries/render-utils/src/Shadow.slh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index e844db43dd..6d3b717032 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -68,13 +68,11 @@ vec2 PCFkernel[4] = vec2[4]( ); float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { - // PCF is buggy so disable it for the time being -#if 0 - float pcfRadius = 3.0; +#if 1 float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html - vec2 offset = pcfRadius * step(fract(position.xy), vec2(0.5, 0.5)); + vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); float shadowAttenuation = (0.25 * ( fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + From 6cd4da877e7c58c116b04fc5b7886158e9bb510c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:11:04 +0100 Subject: [PATCH 008/137] Fixed dithered PCF filter in shadow --- libraries/render-utils/src/Shadow.slh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 6d3b717032..300fa5e7b9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -68,7 +68,6 @@ vec2 PCFkernel[4] = vec2[4]( ); float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { -#if 1 float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html @@ -80,9 +79,6 @@ float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) )); -#else - float shadowAttenuation = fetchShadow(shadowTexcoord.xyz); -#endif return shadowAttenuation; } From cbd28775246409df2e70b7f8ab2151d34ad061ba Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:26:20 +0100 Subject: [PATCH 009/137] Optimized shadow shader evaluation by computing concatenated shadow reprojection matrix on CPU --- libraries/render-utils/src/LightStage.cpp | 11 +++++++---- libraries/render-utils/src/LightStage.h | 6 ++++-- libraries/render-utils/src/Shadow.slh | 18 ++++-------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 411f179d49..eb712b075c 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -14,6 +14,11 @@ #include "LightStage.h" std::string LightStage::_stageName { "LIGHT_STAGE"}; +const glm::mat4 LightStage::Shadow::_biasMatrix{ + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0 }; LightStage::LightStage() { } @@ -92,8 +97,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, _frustum->calculate(); // Update the buffer - _schemaBuffer.edit().projection = ortho; - _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); + _schemaBuffer.edit().reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { @@ -102,8 +106,7 @@ void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { *_frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().projection = shadowFrustum.getProjection(); - _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); + _schemaBuffer.edit().reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } const glm::mat4& LightStage::Shadow::getView() const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 3a2a77055f..c45627c89b 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -62,6 +62,9 @@ public: gpu::TexturePointer map; protected: + + static const glm::mat4 _biasMatrix; + model::LightPointer _light; std::shared_ptr _frustum; @@ -70,8 +73,7 @@ public: Schema(); - glm::mat4 projection; - glm::mat4 viewInverse; + glm::mat4 reprojection; glm::float32 bias; glm::float32 scale; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 300fa5e7b9..8abbc1de3e 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -15,8 +15,7 @@ uniform sampler2DShadow shadowMap; struct ShadowTransform { - mat4 projection; - mat4 viewInverse; + mat4 reprojection; float bias; float scale; @@ -26,12 +25,8 @@ uniform shadowTransformBuffer { ShadowTransform _shadowTransform; }; -mat4 getShadowViewInverse() { - return _shadowTransform.viewInverse; -} - -mat4 getShadowProjection() { - return _shadowTransform.projection; +mat4 getShadowReprojection() { + return _shadowTransform.reprojection; } float getShadowScale() { @@ -44,14 +39,9 @@ float getShadowBias() { // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(vec4 position) { - mat4 biasMatrix = mat4( - 0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.5, 0.5, 0.5, 1.0); float bias = -getShadowBias(); - vec4 shadowCoord = biasMatrix * getShadowProjection() * getShadowViewInverse() * position; + vec4 shadowCoord = getShadowReprojection() * position; return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); } From 7fd03bed7e1c71276f4f3e8c3f785215701cf4f4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 14:28:35 +0100 Subject: [PATCH 010/137] Added cascades in schema buffer but still only first used --- libraries/render-utils/src/LightStage.cpp | 13 ++++---- libraries/render-utils/src/LightStage.h | 8 ++--- libraries/render-utils/src/Shadow.slh | 15 ++++------ libraries/render-utils/src/Shadows_shared.slh | 30 +++++++++++++++++++ 4 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 libraries/render-utils/src/Shadows_shared.slh diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index eb712b075c..d5dfe92b1d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,10 +23,11 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ LightStage::LightStage() { } -LightStage::Shadow::Schema::Schema() : - bias{ 0.005f }, - scale{ 1.0f / MAP_SIZE } { - +LightStage::Shadow::Schema::Schema() { + ShadowTransform defaultTransform; + defaultTransform.bias = 0.005f; + std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); + invMapSize = 1.0f / MAP_SIZE; } LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } { @@ -97,7 +98,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, _frustum->calculate(); // Update the buffer - _schemaBuffer.edit().reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { @@ -106,7 +107,7 @@ void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { *_frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } const glm::mat4& LightStage::Shadow::getView() const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index c45627c89b..3324cfbe99 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -68,15 +68,13 @@ public: model::LightPointer _light; std::shared_ptr _frustum; - class Schema { +#include "Shadows_shared.slh" + + class Schema : public ShadowParameters { public: Schema(); - glm::mat4 reprojection; - - glm::float32 bias; - glm::float32 scale; }; UniformBufferView _schemaBuffer = nullptr; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 8abbc1de3e..1f819e95c9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -14,27 +14,22 @@ // the shadow texture uniform sampler2DShadow shadowMap; -struct ShadowTransform { - mat4 reprojection; - - float bias; - float scale; -}; +<@include Shadows_shared.slh@> uniform shadowTransformBuffer { - ShadowTransform _shadowTransform; + ShadowParameters shadow; }; mat4 getShadowReprojection() { - return _shadowTransform.reprojection; + return shadow.cascades[0].reprojection; } float getShadowScale() { - return _shadowTransform.scale; + return shadow.invMapSize; } float getShadowBias() { - return _shadowTransform.bias; + return shadow.cascades[0].bias; } // Compute the texture coordinates from world coordinates diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh new file mode 100644 index 0000000000..9e462568ad --- /dev/null +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -0,0 +1,30 @@ +// glsl / C++ compatible source as interface for Shadows +#ifdef __cplusplus +# define MAT4 glm::mat4 +# define VEC3 glm::vec3 +#else +# define MAT4 mat4 +# define VEC3 ve3 +#endif + +#define SHADOW_CASCADE_MAX_COUNT 4 + +struct ShadowTransform { + MAT4 reprojection; + + float bias; + vec3 _padding; +}; + +struct ShadowParameters { + int cascadeCount; + float invMapSize; + float _padding1; + float _padding2; + ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; +}; + +// <@if 1@> +// Trigger Scribe include +// <@endif@> +// From e0b36b800f151584c0267f9d1125a658c044125b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 14:55:49 +0100 Subject: [PATCH 011/137] Added Cascade subclass in LightStage Shadow --- .../render-utils/src/DebugDeferredBuffer.cpp | 2 +- .../src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 53 +++++++++++-------- libraries/render-utils/src/LightStage.h | 34 ++++++++---- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 8 +-- 6 files changed, 64 insertions(+), 37 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index d334a53fa1..f8bb7b7b54 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -438,7 +438,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - batch.setResourceTexture(Shadow, globalShadow->map); + batch.setResourceTexture(Shadow, globalShadow->getCascade(0).map); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index dcb16c08f8..a99eb5ffed 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -503,7 +503,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Bind the shadow buffer if (globalShadow) { - batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map); + batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->getCascade(0).map); } auto& program = deferredLightingEffect->_directionalSkyboxLight; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index d5dfe92b1d..e53fe27836 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -28,20 +28,34 @@ LightStage::Shadow::Schema::Schema() { defaultTransform.bias = 0.005f; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; + cascadeCount = 1; } -LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } { +LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); map = framebuffer->getDepthStencilBuffer(); - Schema schema; - _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); } -void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, +const glm::mat4& LightStage::Shadow::Cascade::getView() const { + return _frustum->getView(); +} + +const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { + return _frustum->getProjection(); +} + +LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { + Schema schema; + _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); + _cascades.resize(cascadeCount); +} + +void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); + assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum const auto& direction = glm::normalize(_light->getDirection()); @@ -55,12 +69,15 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, auto up = glm::normalize(glm::cross(side, direction)); orientation = glm::quat_cast(glm::mat3(side, up, -direction)); } - _frustum->setOrientation(orientation); + + auto& cascade = _cascades[cascadeIndex]; + + cascade._frustum->setOrientation(orientation); // Position the keylight frustum - _frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); + cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); - const Transform view{ _frustum->getView()}; + const Transform view{ cascade._frustum->getView()}; const Transform viewInverse{ view.getInverseMatrix() }; auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance); @@ -92,30 +109,24 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, auto near = glm::max(max.z, -nearDepth); auto far = -min.z; glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); - _frustum->setProjection(ortho); + cascade._frustum->setProjection(ortho); // Calculate the frustum's internal state - _frustum->calculate(); + cascade._frustum->calculate(); // Update the buffer - _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } -void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { +void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { + assert(cascadeIndex < _cascades.size()); const Transform view{ shadowFrustum.getView() }; const Transform viewInverse{ view.getInverseMatrix() }; + auto& cascade = _cascades[cascadeIndex]; - *_frustum = shadowFrustum; + *cascade._frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); -} - -const glm::mat4& LightStage::Shadow::getView() const { - return _frustum->getView(); -} - -const glm::mat4& LightStage::Shadow::getProjection() const { - return _frustum->getProjection(); + _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } LightStage::Index LightStage::findLight(const LightPointer& light) const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 3324cfbe99..2f5699c28d 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -46,27 +46,43 @@ public: using UniformBufferView = gpu::BufferView; static const int MAP_SIZE = 1024; - Shadow(model::LightPointer light); + class Cascade { + friend Shadow; + public: - void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + Cascade(); - void setFrustum(const ViewFrustum& shadowFrustum); - const std::shared_ptr getFrustum() const { return _frustum; } + gpu::FramebufferPointer framebuffer; + gpu::TexturePointer map; - const glm::mat4& getView() const; - const glm::mat4& getProjection() const; + const std::shared_ptr& getFrustum() const { return _frustum; } + + const glm::mat4& getView() const; + const glm::mat4& getProjection() const; + + private: + + std::shared_ptr _frustum; + }; + + Shadow(model::LightPointer light, unsigned int cascadeCount = 1); + + void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } - gpu::FramebufferPointer framebuffer; - gpu::TexturePointer map; + unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); } + const Cascade& getCascade(unsigned int index) const { return _cascades[index]; } protected: + using Cascades = std::vector; + static const glm::mat4 _biasMatrix; model::LightPointer _light; - std::shared_ptr _frustum; + Cascades _cascades; #include "Shadows_shared.slh" diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 75af8506a2..0a80ca4ade 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -545,7 +545,7 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { - updateFrustum(*globalShadow->getFrustum(), _shadowFrustumMeshVertices); + updateFrustum(*globalShadow->getCascade(0).getFrustum(), _shadowFrustumMeshVertices); } } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7a6e3dc74f..ce6e78093d 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -139,7 +139,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con auto shadow = lightStage->getCurrentKeyShadow(); if (!shadow) return; - const auto& fbo = shadow->framebuffer; + const auto& fbo = shadow->getCascade(0).framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -149,7 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(adjustedShadowFrustum); + shadow->setFrustum(0, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -252,10 +252,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O auto nearClip = args->getViewFrustum().getNearClip(); float nearDepth = -args->_boomOffset.z; const float SHADOW_MAX_DISTANCE = 20.0f; - globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(0, args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getFrustum())); + args->pushViewFrustum(*(globalShadow->getCascade(0).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } } From 8f84e5fbed3c592a73621e01723dca3b4bb7f81c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 15:39:26 +0100 Subject: [PATCH 012/137] Added sampler array in shader to support future cascades --- .../src/DeferredLightingEffect.cpp | 15 ++++--- libraries/render-utils/src/Shadow.slh | 44 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index a99eb5ffed..831dc86452 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -58,7 +58,7 @@ enum DeferredShader_MapSlot { DEFERRED_BUFFER_DEPTH_UNIT = 3, DEFERRED_BUFFER_OBSCURANCE_UNIT = 4, SHADOW_MAP_UNIT = 5, - SKYBOX_MAP_UNIT = 6, + SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + SHADOW_CASCADE_MAX_COUNT, DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, DEFERRED_BUFFER_CURVATURE_UNIT, DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, @@ -156,7 +156,7 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), DEFERRED_BUFFER_EMISSIVE_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), DEFERRED_BUFFER_DEPTH_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("obscuranceMap"), DEFERRED_BUFFER_OBSCURANCE_UNIT)); - slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), SHADOW_MAP_UNIT)); + slotBindings.insert(gpu::Shader::Binding(std::string("shadowMaps"), SHADOW_MAP_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), SKYBOX_MAP_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT)); @@ -501,9 +501,11 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; - // Bind the shadow buffer + // Bind the shadow buffers if (globalShadow) { - batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->getCascade(0).map); + for (auto i = 0; i < globalShadow->getCascadeCount(); i++) { + batch.setResourceTexture(SHADOW_MAP_UNIT+i, globalShadow->getCascade(i).map); + } } auto& program = deferredLightingEffect->_directionalSkyboxLight; @@ -567,8 +569,9 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); - - batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { + batch.setResourceTexture(SHADOW_MAP_UNIT+i, nullptr); + } } } diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 1f819e95c9..46661b3b8a 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,38 +11,38 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> -// the shadow texture -uniform sampler2DShadow shadowMap; - <@include Shadows_shared.slh@> +// the shadow texture +uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; + uniform shadowTransformBuffer { ShadowParameters shadow; }; -mat4 getShadowReprojection() { - return shadow.cascades[0].reprojection; +mat4 getShadowReprojection(int cascadeIndex) { + return shadow.cascades[cascadeIndex].reprojection; } float getShadowScale() { return shadow.invMapSize; } -float getShadowBias() { - return shadow.cascades[0].bias; +float getShadowBias(int cascadeIndex) { + return shadow.cascades[cascadeIndex].bias; } // Compute the texture coordinates from world coordinates -vec4 evalShadowTexcoord(vec4 position) { - float bias = -getShadowBias(); +vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { + float bias = -getShadowBias(cascadeIndex); - vec4 shadowCoord = getShadowReprojection() * position; + vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); } // Sample the shadowMap with PCF (built-in) -float fetchShadow(vec3 shadowTexcoord) { - return texture(shadowMap, shadowTexcoord); +float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) { + return texture(shadowMaps[cascadeIndex], shadowTexcoord); } vec2 PCFkernel[4] = vec2[4]( @@ -52,24 +52,24 @@ vec2 PCFkernel[4] = vec2[4]( vec2(0.5, -1.5) ); -float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { +float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); float shadowAttenuation = (0.25 * ( - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) )); return shadowAttenuation; } -float evalShadowAttenuation(vec4 position) { - vec4 shadowTexcoord = evalShadowTexcoord(position); +float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { + vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { @@ -77,7 +77,11 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(position, shadowTexcoord); + return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); +} + +float evalShadowAttenuation(vec4 position) { + return evalShadowCascadeAttenuation(0, position); } <@endif@> From 08b06281f4592d472f55cee1e0be394e17706a5e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 16:17:09 +0100 Subject: [PATCH 013/137] Min / Max cascade distance computation --- libraries/render-utils/src/LightStage.cpp | 5 +++- libraries/render-utils/src/LightStage.h | 2 ++ .../render-utils/src/RenderShadowTask.cpp | 23 ++++++++++++++----- libraries/render-utils/src/RenderShadowTask.h | 6 +++++ libraries/render-utils/src/Shadows_shared.slh | 4 +++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index e53fe27836..a52e5bb1ed 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -115,7 +115,10 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->calculate(); // Update the buffer - _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; + schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + schemaCascade.minDistance = viewMinShadowDistance; + schemaCascade.maxDistance = viewMaxShadowDistance; } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 2f5699c28d..ab0a9ed8a0 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -94,6 +94,8 @@ public: }; UniformBufferView _schemaBuffer = nullptr; + void setupCascades(); + friend class Light; }; using ShadowPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index ce6e78093d..5da25cb860 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -215,7 +215,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto cachedMode = task.addJob("ShadowSetup"); + const auto cachedMode = task.addJob("ShadowSetup", 0); // CPU jobs: // Fetch and cull the items from the scene @@ -249,13 +249,24 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O RenderArgs* args = renderContext->args; output = args->_renderMode; - auto nearClip = args->getViewFrustum().getNearClip(); - float nearDepth = -args->_boomOffset.z; - const float SHADOW_MAX_DISTANCE = 20.0f; - globalShadow->setKeylightFrustum(0, args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + const auto nearClip = args->getViewFrustum().getNearClip(); + const auto farClip = args->getViewFrustum().getFarClip(); + const auto nearDepth = -args->_boomOffset.z; + + static const float SHADOW_MAX_DISTANCE = 25.0f; + static const float SHADOW_OVERLAP_DISTANCE = 1.0f; + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); + float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; + + if (_cascadeIndex == 0) { + minCascadeDistance = nearDepth; + } + minCascadeDistance = std::max(minCascadeDistance, nearDepth); + maxCascadeDistance = std::min(maxCascadeDistance, farClip); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getCascade(0).getFrustum())); + args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 7b2bbeb306..99f6e1ecc1 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -56,7 +56,13 @@ class RenderShadowSetup { public: using Output = RenderArgs::RenderMode; using JobModel = render::Job::ModelO; + + RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, Output& output); + +private: + + unsigned int _cascadeIndex; }; class RenderShadowTeardown { diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 9e462568ad..d5002df9a8 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,7 +13,9 @@ struct ShadowTransform { MAT4 reprojection; float bias; - vec3 _padding; + float minDistance; + float maxDistance; + float _padding; }; struct ShadowParameters { From 103e036b70f50dc63295f9b16cbac5ecc5f030f0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 18:30:37 +0100 Subject: [PATCH 014/137] Shadow maps rendered in cascades --- .../src/RenderableZoneEntityItem.cpp | 2 +- .../render-utils/src/DebugDeferredBuffer.cpp | 8 +- .../render-utils/src/DebugDeferredBuffer.h | 5 +- libraries/render-utils/src/LightStage.cpp | 5 +- libraries/render-utils/src/LightStage.h | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 33 ++- .../render-utils/src/RenderDeferredTask.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 58 +++-- libraries/render-utils/src/RenderShadowTask.h | 11 +- libraries/render/src/render/CullTask.cpp | 235 +++++++++--------- libraries/render/src/render/CullTask.h | 22 +- libraries/render/src/render/Item.h | 4 + .../src/render/RenderFetchCullSortTask.cpp | 6 +- .../utilities/render/deferredLighting.qml | 5 +- 14 files changed, 226 insertions(+), 178 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 54cc888c35..7e57eea53f 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -116,7 +116,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { // Do we need to allocate the light in the stage ? if (LightStage::isIndexInvalid(_sunIndex)) { _sunIndex = _stage->addLight(_sunLight); - _shadowIndex = _stage->addShadow(_sunIndex); + _shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT); } else { _stage->updateLightArrayBuffer(_sunIndex); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index f8bb7b7b54..1a95329724 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -284,7 +284,10 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_SCATTERING_SHADER; case LightingMode: return DEFAULT_LIGHTING_SHADER; - case ShadowMode: + case Shadow0Mode: + case Shadow1Mode: + case Shadow2Mode: + case Shadow3Mode: return DEFAULT_SHADOW_SHADER; case LinearDepthMode: return DEFAULT_LINEAR_DEPTH_SHADER; @@ -438,7 +441,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - batch.setResourceTexture(Shadow, globalShadow->getCascade(0).map); + const auto cascadeIndex = glm::clamp(_mode - Mode::Shadow0Mode, 0, (int)globalShadow->getCascadeCount() - 1); + batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index bd5618f5be..49e53c4617 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -64,7 +64,10 @@ protected: LightmapMode, ScatteringMode, LightingMode, - ShadowMode, + Shadow0Mode, + Shadow1Mode, + Shadow2Mode, + Shadow3Mode, LinearDepthMode, HalfLinearDepthMode, HalfNormalMode, diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index a52e5bb1ed..c3a4ebf137 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -19,6 +19,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0 }; +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; LightStage::LightStage() { } @@ -168,11 +169,11 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { } } -LightStage::Index LightStage::addShadow(Index lightIndex) { +LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) { auto light = getLight(lightIndex); Index shadowId = INVALID_INDEX; if (light) { - shadowId = _shadows.newElement(std::make_shared(light)); + shadowId = _shadows.newElement(std::make_shared(light, cascadeCount)); _descs[lightIndex].shadowId = shadowId; } return shadowId; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ab0a9ed8a0..0f77977f2b 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -31,6 +31,8 @@ public: static std::string _stageName; static const std::string& getName() { return _stageName; } + static const unsigned int SUN_SHADOW_CASCADE_COUNT; + using Index = render::indexed_container::Index; static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX }; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } @@ -109,7 +111,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex); + Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 0a80ca4ade..53d280b146 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -533,19 +533,25 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); _viewFrustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); _viewFrustumMeshStream.addBuffer(_viewFrustumMeshVertices._buffer, _viewFrustumMeshVertices._offset, _viewFrustumMeshVertices._stride); - _shadowFrustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); - _shadowFrustumMeshStream.addBuffer(_shadowFrustumMeshVertices._buffer, _shadowFrustumMeshVertices._offset, _shadowFrustumMeshVertices._stride); + for (auto i = 0; i < MAX_SHADOW_FRUSTUM_COUNT; i++) { + _shadowFrustumMeshVertices[i] = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); + _shadowFrustumMeshStream[i].addBuffer(_shadowFrustumMeshVertices[i]._buffer, _shadowFrustumMeshVertices[i]._offset, _shadowFrustumMeshVertices[i]._stride); + } } + auto lightStage = renderContext->_scene->getStage(); + assert(lightStage); + + const auto globalShadow = lightStage->getCurrentKeyShadow(); + if (_updateFrustums) { updateFrustum(args->getViewFrustum(), _viewFrustumMeshVertices); - auto lightStage = renderContext->_scene->getStage(); - assert(lightStage); - - const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { - updateFrustum(*globalShadow->getCascade(0).getFrustum(), _shadowFrustumMeshVertices); + const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount()); + for (auto i = 0; i < cascadeCount; i++) { + updateFrustum(*globalShadow->getCascade(i).getFrustum(), _shadowFrustumMeshVertices[i]); + } } } @@ -583,9 +589,16 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { batch.setInputStream(0, _viewFrustumMeshStream); batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); - batch._glUniform4f(0, 1.0f, 0.0f, 0.0f, 1.0f); - batch.setInputStream(0, _shadowFrustumMeshStream); - batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); + if (globalShadow) { + const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount()); + for (auto i = 0; i < cascadeCount; i++) { + float cascadeTint = i / (float)(globalShadow->getCascadeCount() - 1); + + batch._glUniform4f(0, 1.0f, 0.0f, cascadeTint, 1.0f); + batch.setInputStream(0, _shadowFrustumMeshStream[i]); + batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); + } + } args->_batch = nullptr; }); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e8dd22359d..8f969f5225 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -193,13 +193,15 @@ public: private: + static const int MAX_SHADOW_FRUSTUM_COUNT{ 4 }; + bool _updateFrustums{ true }; gpu::PipelinePointer _pipeline; gpu::BufferView _frustumMeshIndices; gpu::BufferView _viewFrustumMeshVertices; - gpu::BufferView _shadowFrustumMeshVertices; gpu::BufferStream _viewFrustumMeshStream; - gpu::BufferStream _shadowFrustumMeshStream; + gpu::BufferView _shadowFrustumMeshVertices[MAX_SHADOW_FRUSTUM_COUNT]; + gpu::BufferStream _shadowFrustumMeshStream[MAX_SHADOW_FRUSTUM_COUNT]; static void updateFrustum(const ViewFrustum& frustum, gpu::BufferView& vertexBuffer); }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 5da25cb860..d9af9659e5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -137,9 +137,11 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con assert(lightStage); auto shadow = lightStage->getCurrentKeyShadow(); - if (!shadow) return; + if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) { + return; + } - const auto& fbo = shadow->getCascade(0).framebuffer; + const auto& fbo = shadow->getCascade(_cascadeIndex).framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -149,7 +151,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(0, adjustedShadowFrustum); + shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -215,22 +217,25 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto cachedMode = task.addJob("ShadowSetup", 0); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { + const auto setupOutput = task.addJob("ShadowSetup", i); + const auto shadowFilter = setupOutput.getN(1); - // CPU jobs: - // Fetch and cull the items from the scene - auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); - const auto culledShadowSelection = task.addJob("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter); + // CPU jobs: + // Fetch and cull the items from the scene + const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); + const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying(); + const auto culledShadowSelection = task.addJob("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW); - // Sort - const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); + // Sort + const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); + const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); - // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber); + // GPU jobs: Render to shadow map + task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", cachedMode); + task.addJob("ShadowTeardown", setupOutput); + } } void RenderShadowTask::configure(const Config& configuration) { @@ -239,15 +244,18 @@ void RenderShadowTask::configure(const Config& configuration) { // Task::configure(configuration); } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); + // Cache old render args + RenderArgs* args = renderContext->args; + + output.edit0() = args->_renderMode; const auto globalShadow = lightStage->getCurrentKeyShadow(); - if (globalShadow) { - // Cache old render args - RenderArgs* args = renderContext->args; - output = args->_renderMode; + const auto globalShadowCascadeCount = globalShadow->getCascadeCount(); + if (globalShadow && _cascadeIndexgetViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); @@ -255,7 +263,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O static const float SHADOW_MAX_DISTANCE = 25.0f; static const float SHADOW_OVERLAP_DISTANCE = 1.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadowCascadeCount - 1 - _cascadeIndex); float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; if (_cascadeIndex == 0) { @@ -268,6 +276,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + } else { + output.edit1() = ItemFilter::Builder::nothing(); } } @@ -275,6 +285,8 @@ void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext RenderArgs* args = renderContext->args; // Reset the render args - args->popViewFrustum(); - args->_renderMode = input; + args->_renderMode = input.get0(); + if (!input.get1().selectsNothing()) { + args->popViewFrustum(); + } }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 99f6e1ecc1..3dcdfdd9b1 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -24,11 +24,12 @@ public: using Inputs = render::VaryingSet2; using JobModel = render::Job::ModelI; - RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + RenderShadowMap(render::ShapePlumberPointer shapePlumber, unsigned int cascadeIndex) : _shapePlumber{ shapePlumber }, _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; + unsigned int _cascadeIndex; }; class RenderShadowTaskConfig : public render::Task::Config::Persistent { @@ -54,11 +55,11 @@ public: class RenderShadowSetup { public: - using Output = RenderArgs::RenderMode; - using JobModel = render::Job::ModelO; + using Outputs = render::VaryingSet2; + using JobModel = render::Job::ModelO; RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} - void run(const render::RenderContextPointer& renderContext, Output& output); + void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -67,7 +68,7 @@ private: class RenderShadowTeardown { public: - using Input = RenderArgs::RenderMode; + using Input = RenderShadowSetup::Outputs; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 4fc53d99f9..70331cdb47 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -82,28 +82,30 @@ void FetchSpatialTree::configure(const Config& config) { _lodAngle = config.lodAngle; } -void FetchSpatialTree::run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - RenderArgs* args = renderContext->args; - auto& scene = renderContext->_scene; - +void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) { // start fresh outSelection.clear(); - // Eventually use a frozen frustum - auto queryFrustum = args->getViewFrustum(); - if (_freezeFrustum) { - if (_justFrozeFrustum) { - _justFrozeFrustum = false; - _frozenFrutstum = args->getViewFrustum(); - } - queryFrustum = _frozenFrutstum; - } + if (!filter.selectsNothing()) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + auto& scene = renderContext->_scene; - // Octree selection! - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); - scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle); + // Eventually use a frozen frustum + auto queryFrustum = args->getViewFrustum(); + if (_freezeFrustum) { + if (_justFrozeFrustum) { + _justFrozeFrustum = false; + _frozenFrustum = args->getViewFrustum(); + } + queryFrustum = _frozenFrustum; + } + + // Octree selection! + float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle); + } } void CullSpatialSelection::configure(const Config& config) { @@ -113,11 +115,12 @@ void CullSpatialSelection::configure(const Config& config) { } void CullSpatialSelection::run(const RenderContextPointer& renderContext, - const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { + const Inputs& inputs, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; + auto& inSelection = inputs.get0(); auto& details = args->_details.edit(_detailType); details._considered += (int)inSelection.numItems(); @@ -126,9 +129,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (_freezeFrustum) { if (_justFrozeFrustum) { _justFrozeFrustum = false; - _frozenFrutstum = args->getViewFrustum(); + _frozenFrustum = args->getViewFrustum(); } - args->pushViewFrustum(_frozenFrutstum); // replace the true view frustum by the frozen one + args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one } // Culling Frustum / solidAngle test helper class @@ -181,122 +184,124 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, outItems.clear(); outItems.reserve(inSelection.numItems()); - // Now get the bound, and - // filter individually against the _filter - // visibility cull if partially selected ( octree cell contianing it was partial) - // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) + const auto filter = inputs.get1(); + if (!filter.selectsNothing()) { + // Now get the bound, and + // filter individually against the _filter + // visibility cull if partially selected ( octree cell contianing it was partial) + // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) - if (_skipCulling) { - // inside & fit items: filter only, culling is disabled - { - PerformanceTimer perfTimer("insideFitItems"); - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // inside & subcell items: filter only, culling is disabled - { - PerformanceTimer perfTimer("insideSmallItems"); - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // partial & fit items: filter only, culling is disabled - { - PerformanceTimer perfTimer("partialFitItems"); - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // partial & subcell items: filter only, culling is disabled - { - PerformanceTimer perfTimer("partialSmallItems"); - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - } else { - - // inside & fit items: easy, just filter - { - PerformanceTimer perfTimer("insideFitItems"); - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // inside & subcell items: filter & distance cull - { - PerformanceTimer perfTimer("insideSmallItems"); - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.solidAngleTest(itemBound.bound)) { + if (_skipCulling) { + // inside & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & fit items: filter & frustum cull - { - PerformanceTimer perfTimer("partialFitItems"); - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // inside & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & subcell items:: filter & frutum cull & solidangle cull - { - PerformanceTimer perfTimer("partialSmallItems"); - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // partial & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + } else { + + // inside & fit items: easy, just filter + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // inside & subcell items: filter & distance cull + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); } } } } + + // partial & fit items: filter & frustum cull + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + + // partial & subcell items:: filter & frutum cull & solidangle cull + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + } } } details._rendered += (int)outItems.size(); - // Restore frustum if using the frozen one: if (_freezeFrustum) { args->popViewFrustum(); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index fae2a342a1..486c4f4cdf 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -51,19 +51,16 @@ namespace render { class FetchSpatialTree { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; - ViewFrustum _frozenFrutstum; + ViewFrustum _frozenFrustum; float _lodAngle; public: using Config = FetchSpatialTreeConfig; - using JobModel = Job::ModelO; + using JobModel = Job::ModelIO; FetchSpatialTree() {} - FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {} - - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); - void run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection); + void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection); }; class CullSpatialSelectionConfig : public Job::Config { @@ -88,25 +85,24 @@ namespace render { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; bool _skipCulling{ false }; - ViewFrustum _frozenFrutstum; + ViewFrustum _frozenFrustum; public: using Config = CullSpatialSelectionConfig; - using JobModel = Job::ModelIO; + using Inputs = render::VaryingSet2; + using JobModel = Job::ModelIO; - CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type, const ItemFilter& filter) : + CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type) : _cullFunctor{ cullFunctor }, - _detailType(type), - _filter(filter) {} + _detailType(type) {} CullSpatialSelection(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} CullFunctor _cullFunctor; RenderDetails::Type _detailType{ RenderDetails::OTHER }; - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); - void run(const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 2b02db81f9..77f5910b9e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -182,6 +182,8 @@ public: Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } @@ -191,12 +193,14 @@ public: static Builder background() { return Builder().withViewSpace().withLayered(); } static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } + static Builder nothing() { return Builder().withNothing(); } }; ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} // Item Filter operator testing if a key pass the filter bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } + bool selectsNothing() const { return !_mask.any(); } class Less { public: diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index b9f65f48a0..d7294fa2bd 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -22,9 +22,11 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene - auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); - const auto culledSpatialSelection = task.addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); + const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); + const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled const auto nonspatialSelection = task.addJob("FetchOverlaySelection"); diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 86a16d9a25..def8b1fcd1 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -184,7 +184,10 @@ Rectangle { ListElement { text: "Lightmap"; color: "White" } ListElement { text: "Scattering"; color: "White" } ListElement { text: "Lighting"; color: "White" } - ListElement { text: "Shadow"; color: "White" } + ListElement { text: "Shadow Cascade 0"; color: "White" } + ListElement { text: "Shadow Cascade 1"; color: "White" } + ListElement { text: "Shadow Cascade 2"; color: "White" } + ListElement { text: "Shadow Cascade 3"; color: "White" } ListElement { text: "Linear Depth"; color: "White" } ListElement { text: "Half Linear Depth"; color: "White" } ListElement { text: "Half Normal"; color: "White" } From c77a12f5fa5c0af32248ec58086ba6bdb0e72ea2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Nov 2017 07:55:57 +1300 Subject: [PATCH 015/137] Update Clipboard and Menu JSDoc to use JavaScript type names --- .../scripting/ClipboardScriptingInterface.h | 22 ++++---- .../src/scripting/MenuScriptingInterface.h | 54 +++++++++---------- .../script-engine/src/MenuItemProperties.cpp | 20 +++---- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index e9d2e0dd3b..c1edf09f2a 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -38,7 +38,7 @@ public: /**jsdoc * Compute the largest dimension of the extents of the contents held in the clipboard. * @function Clipboard.getClipboardContentsLargestDimension - * @returns {float} The largest dimension computed. + * @returns {Number} The largest dimension computed. */ Q_INVOKABLE float getClipboardContentsLargestDimension(); @@ -46,29 +46,29 @@ public: * Import entities from a JSON file containing entity data into the clipboard. * You can generate a JSON file using {@link Clipboard.exportEntities}. * @function Clipboard.importEntities - * @param {string} filename Path and name of file to import. - * @returns {bool} `true` if the import was successful, otherwise `false`. + * @param {String} filename Path and name of file to import. + * @returns {Boolean} `true` if the import was successful, otherwise `false`. */ Q_INVOKABLE bool importEntities(const QString& filename); /**jsdoc * Export the entities specified to a JSON file. * @function Clipboard.exportEntities - * @param {string} filename Path and name of the file to export entities to. Should have the extension `.json`. + * @param {String} filename Path and name of the file to export entities to. Should have the extension `.json`. * @param {EntityID[]} entityIDs IDs of entities to export. - * @returns {bool} `true` if the export was successful, otherwise `false`. + * @returns {Boolean} `true` if the export was successful, otherwise `false`. */ Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); /**jsdoc * Export the entities with centers within a cube to a JSON file. * @function Clipboard.exportEntities - * @param {string} filename Path and name of the file to export entities to. Should have the extension `.json`. - * @param {float} x X-coordinate of the cube center. - * @param {float} y Y-coordinate of the cube center. - * @param {float} z Z-coordinate of the cube center. - * @param {float} scale Half dimension of the cube. - * @returns {bool} `true` if the export was successful, otherwise `false`. + * @param {String} filename Path and name of the file to export entities to. Should have the extension `.json`. + * @param {Number} x X-coordinate of the cube center. + * @param {Number} y Y-coordinate of the cube center. + * @param {Number} z Z-coordinate of the cube center. + * @param {Number} scale Half dimension of the cube. + * @returns {Boolean} `true` if the export was successful, otherwise `false`. */ Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale); diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index dd35947900..a05f7460b9 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -55,23 +55,23 @@ public slots: /**jsdoc * Add a new top-level menu. * @function Menu.addMenu - * @param {string} menuName Name that will be displayed for the menu. - * @param {string} [grouping] Name of the grouping, if any, to add this menu to. + * @param {String} menuName Name that will be displayed for the menu. + * @param {String} [grouping] Name of the grouping, if any, to add this menu to. */ void addMenu(const QString& menuName, const QString& grouping = QString()); /**jsdoc * Remove a top-level menu. * @function Menu.removeMenu - * @param {string} menuName Name of the menu to remove. + * @param {String} menuName Name of the menu to remove. */ void removeMenu(const QString& menuName); /**jsdoc * Check whether a top-level menu exists. * @function Menu.menuExists - * @param {string} menuName Name of the menu to check for existence. - * @returns {bool} `true` if the menu exists, otherwise `false`. + * @param {String} menuName Name of the menu to check for existence. + * @returns {Boolean} `true` if the menu exists, otherwise `false`. */ bool menuExists(const QString& menuName); @@ -79,16 +79,16 @@ public slots: * Add a separator with an unclickable label below it. * The line will be placed at the bottom of the menu. * @function Menu.addSeparator - * @param {string} menuName Name of the menu to add a separator to. - * @param {string} separatorName Name of the separator that will be displayed as the label below the separator line. + * @param {String} menuName Name of the menu to add a separator to. + * @param {String} separatorName Name of the separator that will be displayed as the label below the separator line. */ void addSeparator(const QString& menuName, const QString& separatorName); /**jsdoc * Remove a separator from a menu. * @function Menu.removeSeparator - * @param {string} menuName Name of the menu to remove the separator from. - * @param {string} separatorName Name of the separator to remove. + * @param {String} menuName Name of the menu to remove the separator from. + * @param {String} separatorName Name of the separator to remove. */ void removeSeparator(const QString& menuName, const QString& separatorName); @@ -102,9 +102,9 @@ public slots: /**jsdoc * Add a new menu item to a menu. * @function Menu.addMenuItem - * @param {string} menuName Name of the menu to add a menu item to. - * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. - * @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item. + * @param {String} menuName Name of the menu to add a menu item to. + * @param {String} menuItem Name of the menu item. This is what will be displayed in the menu. + * @param {String} [shortcutKey] A shortcut key that can be used to trigger the menu item. */ void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey); void addMenuItem(const QString& menuName, const QString& menuitem); @@ -112,17 +112,17 @@ public slots: /**jsdoc * Remove a menu item from a menu. * @function Menu.removeMenuItem - * @param {string} menuName Name of the menu to remove a menu item from. - * @param {string} menuItem Name of the menu item to remove. + * @param {String} menuName Name of the menu to remove a menu item from. + * @param {String} menuItem Name of the menu item to remove. */ void removeMenuItem(const QString& menuName, const QString& menuitem); /**jsdoc * Check if a menu item exists. * @function Menu.menuItemExists - * @param {string} menuName Name of the menu that the menu item is in. - * @param {string} menuItem Name of the menu item to check for existence of. - * @returns {bool} `true` if the menu item exists, otherwise `false`. + * @param {String} menuName Name of the menu that the menu item is in. + * @param {String} menuItem Name of the menu item to check for existence of. + * @returns {Boolean} `true` if the menu item exists, otherwise `false`. */ bool menuItemExists(const QString& menuName, const QString& menuitem); @@ -136,23 +136,23 @@ public slots: /**jsdoc * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked - * @param {string} menuOption The name of the menu item. - * @returns {bool} `true` if the option is checked, otherwise false. + * @param {String} menuOption The name of the menu item. + * @returns {Boolean} `true` if the option is checked, otherwise false. */ bool isOptionChecked(const QString& menuOption); /**jsdoc * Set a checkable menu item as checked or unchecked. * @function Menu.setIsOptionChecked - * @param {string} menuOption The name of the menu item to modify. - * @param {bool} isChecked If `true`, the menu item will be checked, otherwise it will not be checked. + * @param {String} menuOption The name of the menu item to modify. + * @param {Boolean} isChecked If `true`, the menu item will be checked, otherwise it will not be checked. */ void setIsOptionChecked(const QString& menuOption, bool isChecked); /**jsdoc * Trigger the menu item as if the user clicked on it. * @function Menu.triggerOption - * @param {string} menuOption The name of the menu item to trigger. + * @param {String} menuOption The name of the menu item to trigger. */ void triggerOption(const QString& menuOption); @@ -160,16 +160,16 @@ public slots: * Check whether a menu is enabled. If a menu is disabled it is grayed out and unusable. * Menus are enabled by default. * @function Menu.isMenuEnabled - * @param {string} menuName The name of the menu to check. - * @returns {bool} `true` if the menu is enabled, otherwise false. + * @param {String} menuName The name of the menu to check. + * @returns {Boolean} `true` if the menu is enabled, otherwise false. */ bool isMenuEnabled(const QString& menuName); /**jsdoc * Set a menu to be enabled or disabled. If a menu is disabled it is grayed out and unusable. * @function Menu.setMenuEnabled - * @param {string} menuName The name of the menu to modify. - * @param {bool} isEnabled Whether the menu will be enabled or not. + * @param {String} menuName The name of the menu to modify. + * @param {Boolean} isEnabled Whether the menu will be enabled or not. */ void setMenuEnabled(const QString& menuName, bool isEnabled); @@ -183,7 +183,7 @@ signals: /**jsdoc * Notifies scripts when a menu item is clicked (or triggered by {@link Menu.triggerOption}). * @function Menu.menuItemEvent - * @param {string} menuItem Name of the menu item that was clicked. + * @param {String} menuItem Name of the menu item that was clicked. * @returns {Signal} */ void menuItemEvent(const QString& menuItem); diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index c24e5a7816..5c9aa8eee9 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -57,17 +57,17 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * position. * * @typedef {Object} Menu.MenuItemProperties - * @property {string} menuName Name of the menu. - * @property {string} menuItemName Name of the menu item. - * @property {bool} [isCheckable=false] Whether or not the menu item is checkable. - * @property {bool} [isChecked=false] Whether or not the menu item is checked. - * @property {bool} [isSeparator=false] Whether or not the menu item is a separator. - * @property {string} [shortcutKey] A shortcut key that triggers the menu item. + * @property {String} menuName Name of the menu. + * @property {String} menuItemName Name of the menu item. + * @property {Boolean} [isCheckable=false] Whether or not the menu item is checkable. + * @property {Boolean} [isChecked=false] Whether or not the menu item is checked. + * @property {Boolean} [isSeparator=false] Whether or not the menu item is a separator. + * @property {String} [shortcutKey] A shortcut key that triggers the menu item. * @property {KeyEvent} [shortcutKeyEvent] A {@link KeyEvent} that specifies a key that triggers the menu item. - * @property {int} [position] The position to place the new menu item. `0` is the first menu item. - * @property {string} [beforeItem] The name of the menu item to place this menu item before. - * @property {string} [afterItem] The name of the menu item to place this menu item after. - * @property {string} [grouping] The name of grouping to add this menu item to. + * @property {Number} [position] The position to place the new menu item. An integer number with`0` being the first menu item. + * @property {String} [beforeItem] The name of the menu item to place this menu item before. + * @property {String} [afterItem] The name of the menu item to place this menu item after. + * @property {String} [grouping] The name of grouping to add this menu item to. */ void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) { properties.menuName = object.property("menuName").toVariant().toString(); From 1a06c6ef118257a97674844425ea445b968ae204 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 10 Nov 2017 15:23:17 -0800 Subject: [PATCH 016/137] all entity animation fixes done except play animation --- .../src/RenderableModelEntityItem.cpp | 50 +++++++++++++------ .../src/RenderableModelEntityItem.h | 4 ++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4ebdfb7b8b..989f019980 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1012,24 +1012,33 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto now = usecTimestampNow(); auto interval = now - _lastAnimated; _lastAnimated = now; - //we handle the hold animation property here - //if hold don't advance the current frame. - if (!isHolding) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + + + //here we implement the looping animation property + //if we have played through the animation once then we hold on the last frame + // if (!isLooping && (_currentFrame > _endAnim)) { + //don't advance current frame!!! + + //}else{ + if( isLooping || ( _currentFrame < _endAnim ) ){ + //else advance the current frame. + //if hold or not playing don't advance the current frame. + if (!isHolding && entity->getAnimationIsPlaying()) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + } } { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % updatedFrameCount; - //this starts us at the offset first frame. - animationCurrentFrame += firstFrame; + int animationCurrentFrame = (int)(glm::floor(_currentFrame - _renderAnimationProperties.getFirstFrame())) % updatedFrameCount; + //this gives us the absolute frame value to use by adding the first frame value. + animationCurrentFrame += _renderAnimationProperties.getFirstFrame(); + - //here we implement the looping animation property - //if we have played through the animation once then we hold on the last frame - if (!isLooping && (_currentFrame > (updatedFrameCount - 1))) { - animationCurrentFrame = updatedFrameCount + firstFrame; - } + + + if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; @@ -1352,10 +1361,19 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { withWriteLock([&] { + if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { + _currentFrame = newAnimationProperties.getCurrentFrame(); + _endAnim = (_currentFrame - 1) + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())); + //qCDebug(entitiesrenderer) << "reset current frame" << _endAnim; + } + if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { + //set the end of animation relative to the current frame + //qCDebug(entitiesrenderer) << "turned off looping"; + float startOffset = newAnimationProperties.getCurrentFrame() - newAnimationProperties.getFirstFrame(); + float posRelativeToStart = (_currentFrame - newAnimationProperties.getFirstFrame()) - startOffset; + _endAnim = (_currentFrame - 1) + (((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - ((int)(glm::floor(posRelativeToStart)) % ((int)newAnimationProperties.getLastFrame() - (int)newAnimationProperties.getFirstFrame()))); + } _renderAnimationProperties = newAnimationProperties; - //if (entity->getAnimationHold()) { - // _currentFrame = _renderAnimationProperties.getCurrentFrame(); - //} }); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index d33976e356..65b915465c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -187,6 +187,10 @@ private: float _currentFrame { 0 }; //bool _previousHold{ false }; float _currentFramePropertyValue{ 0 }; + float _firstFramePropertyValue{ 0 }; + float _lastFramePropertyValue{ 0 }; + bool _stopLoop{ false }; + float _endAnim{ 0 }; }; From 3f7a9e97039eb5e5e58d051c0af7384aded17e26 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 10 Nov 2017 15:36:57 -0800 Subject: [PATCH 017/137] all entity animation fixes including the animation play reset --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 989f019980..3ed0bdfc43 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1364,6 +1364,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { _currentFrame = newAnimationProperties.getCurrentFrame(); _endAnim = (_currentFrame - 1) + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())); + _lastAnimated = 0; //qCDebug(entitiesrenderer) << "reset current frame" << _endAnim; } if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { From a576cc04e1fc77131343f5796d0075238f3c2e00 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Nov 2017 16:16:24 +1300 Subject: [PATCH 018/137] Clipboard JSDoc improvements --- .../src/scripting/ClipboardScriptingInterface.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index c1edf09f2a..0c6119009a 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -47,28 +47,28 @@ public: * You can generate a JSON file using {@link Clipboard.exportEntities}. * @function Clipboard.importEntities * @param {String} filename Path and name of file to import. - * @returns {Boolean} `true` if the import was successful, otherwise `false`. + * @returns {Boolean} true if the import was successful, otherwise false. */ Q_INVOKABLE bool importEntities(const QString& filename); /**jsdoc * Export the entities specified to a JSON file. * @function Clipboard.exportEntities - * @param {String} filename Path and name of the file to export entities to. Should have the extension `.json`. - * @param {EntityID[]} entityIDs IDs of entities to export. - * @returns {Boolean} `true` if the export was successful, otherwise `false`. + * @param {String} filename Path and name of the file to export the entities to. Should have the extension ".json". + * @param {EntityID[]} entityIDs Array of IDs of the entities to export. + * @returns {Boolean} true if the export was successful, otherwise false. */ Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); /**jsdoc * Export the entities with centers within a cube to a JSON file. * @function Clipboard.exportEntities - * @param {String} filename Path and name of the file to export entities to. Should have the extension `.json`. + * @param {String} filename Path and name of the file to export the entities to. Should have the extension ".json". * @param {Number} x X-coordinate of the cube center. * @param {Number} y Y-coordinate of the cube center. * @param {Number} z Z-coordinate of the cube center. * @param {Number} scale Half dimension of the cube. - * @returns {Boolean} `true` if the export was successful, otherwise `false`. + * @returns {Boolean} true if the export was successful, otherwise false. */ Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale); From c8734ccd639127526245a285e7596504e8e27deb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Nov 2017 16:18:05 +1300 Subject: [PATCH 019/137] Menu JSDoc improvements --- interface/src/scripting/MenuScriptingInterface.h | 16 ++++++++-------- .../script-engine/src/MenuItemProperties.cpp | 7 ++++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index a05f7460b9..8741568c72 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -19,12 +19,12 @@ class MenuItemProperties; /**jsdoc * The Menu API provides access to the menu that is displayed at the top of the window - * on a user's desktop and when the "MENU" button is pressed on the tablet. + * on a user's desktop and in the tablet when the "MENU" button is pressed. * *

Groupings

* A "grouping" provides a way to group a set of menus or menu items together so * that they can all be set visible or invisible as a group. - * There are two available groups: "Advanced" and "Developer". + * There are two available groups: "Advanced" and "Developer". * These groupings can be toggled in the "Settings" menu. * If a menu item doesn't belong to a group it is always displayed. * @@ -71,7 +71,7 @@ public slots: * Check whether a top-level menu exists. * @function Menu.menuExists * @param {String} menuName Name of the menu to check for existence. - * @returns {Boolean} `true` if the menu exists, otherwise `false`. + * @returns {Boolean} true if the menu exists, otherwise false. */ bool menuExists(const QString& menuName); @@ -122,7 +122,7 @@ public slots: * @function Menu.menuItemExists * @param {String} menuName Name of the menu that the menu item is in. * @param {String} menuItem Name of the menu item to check for existence of. - * @returns {Boolean} `true` if the menu item exists, otherwise `false`. + * @returns {Boolean} true if the menu item exists, otherwise false. */ bool menuItemExists(const QString& menuName, const QString& menuitem); @@ -137,7 +137,7 @@ public slots: * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked * @param {String} menuOption The name of the menu item. - * @returns {Boolean} `true` if the option is checked, otherwise false. + * @returns {Boolean} true if the option is checked, otherwise false. */ bool isOptionChecked(const QString& menuOption); @@ -145,7 +145,7 @@ public slots: * Set a checkable menu item as checked or unchecked. * @function Menu.setIsOptionChecked * @param {String} menuOption The name of the menu item to modify. - * @param {Boolean} isChecked If `true`, the menu item will be checked, otherwise it will not be checked. + * @param {Boolean} isChecked If true, the menu item will be checked, otherwise it will not be checked. */ void setIsOptionChecked(const QString& menuOption, bool isChecked); @@ -161,7 +161,7 @@ public slots: * Menus are enabled by default. * @function Menu.isMenuEnabled * @param {String} menuName The name of the menu to check. - * @returns {Boolean} `true` if the menu is enabled, otherwise false. + * @returns {Boolean} true if the menu is enabled, otherwise false. */ bool isMenuEnabled(const QString& menuName); @@ -169,7 +169,7 @@ public slots: * Set a menu to be enabled or disabled. If a menu is disabled it is grayed out and unusable. * @function Menu.setMenuEnabled * @param {String} menuName The name of the menu to modify. - * @param {Boolean} isEnabled Whether the menu will be enabled or not. + * @param {Boolean} isEnabled If true, the menu will be enabled, otherwise it will be disabled. */ void setMenuEnabled(const QString& menuName, bool isEnabled); diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index 5c9aa8eee9..e35f408bf8 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -53,8 +53,8 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt /**jsdoc * A set of properties that can be passed to {@link Menu.addMenuItem} to create a new menu item. * - * If none of `position`, `beforeItem`, `afterItem`, or `grouping` are specified, the menu item will be placed in the last - * position. + * If none of position, beforeItem, afterItem, or grouping are specified, + * the menu item will be placed at the end of the menu. * * @typedef {Object} Menu.MenuItemProperties * @property {String} menuName Name of the menu. @@ -64,7 +64,8 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * @property {Boolean} [isSeparator=false] Whether or not the menu item is a separator. * @property {String} [shortcutKey] A shortcut key that triggers the menu item. * @property {KeyEvent} [shortcutKeyEvent] A {@link KeyEvent} that specifies a key that triggers the menu item. - * @property {Number} [position] The position to place the new menu item. An integer number with`0` being the first menu item. + * @property {Number} [position] The position to place the new menu item. An integer number with 0 being the first + * menu item. * @property {String} [beforeItem] The name of the menu item to place this menu item before. * @property {String} [afterItem] The name of the menu item to place this menu item after. * @property {String} [grouping] The name of grouping to add this menu item to. From f5e155bf946cd2a29b6e705b1c0b9164ea87cd5b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Nov 2017 17:01:55 +1300 Subject: [PATCH 020/137] Expand Menu's JSDoc --- .../src/scripting/MenuScriptingInterface.h | 55 ++++++++++++++++++- .../script-engine/src/MenuItemProperties.cpp | 2 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 8741568c72..d1e6337e3f 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -22,6 +22,7 @@ class MenuItemProperties; * on a user's desktop and in the tablet when the "MENU" button is pressed. * *

Groupings

+ * * A "grouping" provides a way to group a set of menus or menu items together so * that they can all be set visible or invisible as a group. * There are two available groups: "Advanced" and "Developer". @@ -29,6 +30,23 @@ class MenuItemProperties; * If a menu item doesn't belong to a group it is always displayed. * * @namespace Menu + * + * @example + * Menu.addMenu("Demo Menu"); + * + * Menu.addMenuItem({ + * menuName: "Demo Menu", + * menuItemName: "Demo Item" + * }); + * + * function onMenuItemEvent(menuItem) + * { + * if(menuItem == "Demo Item") { + * print("Nice! You clicked the demo menu item."); + * } + * } + * + * Menu.menuItemEvent.connect(menuItemEventHandler); */ /** @@ -55,8 +73,11 @@ public slots: /**jsdoc * Add a new top-level menu. * @function Menu.addMenu - * @param {String} menuName Name that will be displayed for the menu. + * @param {String} menuName Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. * @param {String} [grouping] Name of the grouping, if any, to add this menu to. + * @example + * Menu.addMenu("Test Menu"); + * Menu.addMenu("Test Menu > Test Sub Menu"); */ void addMenu(const QString& menuName, const QString& grouping = QString()); @@ -64,6 +85,9 @@ public slots: * Remove a top-level menu. * @function Menu.removeMenu * @param {String} menuName Name of the menu to remove. + * @example + * Menu.removeMenu("Test Menu > Test Sub Menu"); + * Menu.removeMenu("Test Menu"); */ void removeMenu(const QString& menuName); @@ -72,6 +96,10 @@ public slots: * @function Menu.menuExists * @param {String} menuName Name of the menu to check for existence. * @returns {Boolean} true if the menu exists, otherwise false. + * @example + * if (Menu.menuExists("Developer")) { + * // Perform a task if there is a Developer menu. + * } */ bool menuExists(const QString& menuName); @@ -81,6 +109,8 @@ public slots: * @function Menu.addSeparator * @param {String} menuName Name of the menu to add a separator to. * @param {String} separatorName Name of the separator that will be displayed as the label below the separator line. + * @example + * Menu.addSeparator("Developer","Test Separator"); */ void addSeparator(const QString& menuName, const QString& separatorName); @@ -89,6 +119,8 @@ public slots: * @function Menu.removeSeparator * @param {String} menuName Name of the menu to remove the separator from. * @param {String} separatorName Name of the separator to remove. + * @example + * Menu.removeSeparator("Developer","Test Separator"); */ void removeSeparator(const QString& menuName, const QString& separatorName); @@ -96,6 +128,14 @@ public slots: * Add a new menu item to a menu. * @function Menu.addMenuItem * @param {Menu.MenuItemProperties} properties Properties of the menu item to create. + * @example + * Menu.addMenuItem({ + * menuName: "Developer", + * menuItemName: "Test", + * afterItem: "Log", + * shortcutKey: "Ctrl+Shift+T", + * grouping: "Advanced" + * }); */ void addMenuItem(const MenuItemProperties& properties); @@ -105,6 +145,8 @@ public slots: * @param {String} menuName Name of the menu to add a menu item to. * @param {String} menuItem Name of the menu item. This is what will be displayed in the menu. * @param {String} [shortcutKey] A shortcut key that can be used to trigger the menu item. + * @example + * Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T"); */ void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey); void addMenuItem(const QString& menuName, const QString& menuitem); @@ -114,6 +156,7 @@ public slots: * @function Menu.removeMenuItem * @param {String} menuName Name of the menu to remove a menu item from. * @param {String} menuItem Name of the menu item to remove. + * Menu.removeMenuItem("Developer", "Test"); */ void removeMenuItem(const QString& menuName, const QString& menuitem); @@ -123,6 +166,10 @@ public slots: * @param {String} menuName Name of the menu that the menu item is in. * @param {String} menuItem Name of the menu item to check for existence of. * @returns {Boolean} true if the menu item exists, otherwise false. + * @example + * if (Menu.menuItemExists("Developer", "Stats")) { + * // Perform a task if there is a Stats item in the Developer menu. + * } */ bool menuItemExists(const QString& menuName, const QString& menuitem); @@ -185,6 +232,12 @@ signals: * @function Menu.menuItemEvent * @param {String} menuItem Name of the menu item that was clicked. * @returns {Signal} + * @example + * function onMenuItemEvent(menuItem) { + * print("You clicked on " + menuItem); + * } + * + * Menu.menuItemEvent.connect(onMenuItemEvent); */ void menuItemEvent(const QString& menuItem); }; diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index e35f408bf8..5caef7933c 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -57,7 +57,7 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * the menu item will be placed at the end of the menu. * * @typedef {Object} Menu.MenuItemProperties - * @property {String} menuName Name of the menu. + * @property {String} menuName Name of the menu. Nested menus can be described using the ">" symbol. * @property {String} menuItemName Name of the menu item. * @property {Boolean} [isCheckable=false] Whether or not the menu item is checkable. * @property {Boolean} [isChecked=false] Whether or not the menu item is checked. From 39ccd7d65647754fd78b86859c21faf1b9a738f6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 12 Nov 2017 11:04:35 +1300 Subject: [PATCH 021/137] Update JSDoc for Camera API --- interface/src/FancyCamera.h | 30 +++++- libraries/shared/src/shared/Camera.h | 154 ++++++++++++++++++++++++--- tools/jsdoc/plugins/hifi.js | 1 + 3 files changed, 165 insertions(+), 20 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 3552dc6ca8..d7cf39dd8c 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -19,10 +19,14 @@ class FancyCamera : public Camera { Q_OBJECT /**jsdoc - * @namespace Camera - * @property cameraEntity {EntityID} The position and rotation properties of - * the entity specified by this ID are then used as the camera's position and - * orientation. Only works when mode is "entity". + * @namespace + * @augments Camera + */ + + // FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h. + /**jsdoc + * @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in + * entity mode. */ Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) @@ -34,7 +38,25 @@ public: public slots: + /**jsdoc + * Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can + * also get the entity ID using the Camera.cameraEntity property. + * @function Camera.getCameraEntity + * @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; null if no camera + * entity has been set. + */ QUuid getCameraEntity() const; + + /**jsdoc + * Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the + * entity using the Camera.cameraEntity property. + * @function Camera.setCameraEntity + * @param {Uuid} entityID The entity that the camera should follow when it's in entity mode. + * @example + * Camera.setModeString("entity"); + * var entity = Entities.findClosestEntity(MyAvatar.position, 100.0); + * Camera.setCameraEntity(entity); + */ void setCameraEntity(QUuid entityID); private: diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 3ad08bd719..ae096256fc 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -37,12 +37,70 @@ class Camera : public QObject { Q_OBJECT /**jsdoc + * The Camera API provides access to the "camera" that defines your view in desktop and HMD modes. + * + *

+ * + *

Modes:
+ * + *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the + * following modes:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ModeStringDescription
First Person"first person"The camera is positioned such that you have the same view as your avatar. The camera moves and rotates with + * your avatar.
Third Person"third person"The camera is positioned such that you have a view from just behind your avatar. The camera moves and rotates + * with your avatar.
Mirror"mirror"The camera is positioned such that you are looking directly at your avatar. The camera moves and rotates with + * your avatar.
Independent"independent"The camera's position and orientation don't change with your avatar movement. Instead, they can be set via + * scripting.
Entity"entity"The camera's position and orientation are set to be the same as a specified entity's, and move with the entity + * as it moves. + *
+ * + *

The camera mode can be changed using Camera.mode and {@link Camera.setModeString}, and Interface's "View" + * menu.

+ * * @namespace Camera - * @property position {Vec3} The position of the camera. - * @property orientation {Quat} The orientation of the camera. - * @property mode {string} The current camera mode. - * @property frustum {Object} The frustum of the camera. + * @property position {Vec3} The position of the camera. You can set this value only when the camera is in entity mode. + * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in entity + * mode. + * @property mode {String} The camera mode. + * @property frustum {Frustum} The camera frustum. + * @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera + * is in entity mode. */ + // FIXME: The cameraEntity property definition is copied from FancyCamera.h. Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) @@ -69,52 +127,116 @@ public: QVariantMap getViewFrustum(); public slots: + /**jsdoc + * Get the current camera mode. You can also get the mode using the Camera.mode property. + * @function Camera.getModeString + * @return {String} The current camera mode. + */ QString getModeString() const; + + /**jsdoc + * Set the camera mode. You can also set the mode using the Camera.mode property. + * @function Camera.setModeString + * @param {String} mode - The mode to set the camera to. + */ void setModeString(const QString& mode); + /**jsdoc + * Get the current camera position. You can also get the position using the Camera.position property. + * @function Camera.getPosition + * @return {Vec3} The current camera position. + */ glm::vec3 getPosition() const { return _position; } + + /**jsdoc + * Set the camera position. You can also set the position using the Camera.position property. Only works if the + * camera is in independent mode. + * @function Camera.setPosition + * @param {Vec3} position - The position to set the camera at. + */ void setPosition(const glm::vec3& position); + /**jsdoc + * Get the current camera orientation. You can also get the orientation using the Camera.orientation property. + * @function Camera.getOrientation + * @return {Quat} The current camera orientation. + */ glm::quat getOrientation() const { return _orientation; } + + /**jsdoc + * Set the camera orientation. You can also set the orientation using the Camera.orientation property. Only + * works if the camera is in independent mode. + * @function Camera.setOrientation + * @param {Quat} orientation - The orientation to set the camera to. + */ void setOrientation(const glm::quat& orientation); /**jsdoc - * Compute a {PickRay} based on the current camera configuration and the position x,y on the screen. + * Compute a {PickRay} based on the current camera configuration and the specified x, y position on the screen. + * The {PickRay} can be used in functions such as {Entities.findRayIntersection} and {Overlays.findRayIntersection}. * @function Camera.computePickRay - * @param {float} x X-coordinate on screen. - * @param {float} y Y-coordinate on screen. + * @param {Number} x - X-coordinate on screen. + * @param {Number} y - Y-coordinate on screen. * @return {PickRay} The computed {PickRay}. + * @example + * function onMousePressEvent(event) { + * var pickRay = Camera.computePickRay(event.x, event.y); + * var intersection = Entities.findRayIntersection(pickRay); + * if (intersection.intersects) { + * print ("You clicked on entity " + intersection.entityID); + * } + * } + * + * Controller.mousePressEvent.connect(onMousePressEvent); */ virtual PickRay computePickRay(float x, float y) const = 0; /**jsdoc - * Set the camera to look at position position. Only works while in independent. - * camera mode. + * Rotate the camera to look at the specified position. Only works if the camera is in independent mode. * @function Camera.lookAt - * @param {Vec3} Position Position to look at. + * @param {Vec3} position - Position to look at. + * @example + * function onMousePressEvent(event) { + * var pickRay = Camera.computePickRay(event.x, event.y); + * var intersection = Entities.findRayIntersection(pickRay); + * if (intersection.intersects) { + * // Switch to independent mode. + * Camera.mode = "independent"; + * // Look at the entity that was clicked. + * var properties = Entities.getEntityProperties(intersection.entityID, "position"); + * Camera.lookAt(properties.position); + * } + * } + * + * Controller.mousePressEvent.connect(onMousePressEvent); */ void lookAt(const glm::vec3& position); /**jsdoc - * Set the camera to continue looking at position position. - * Only works while in `independent` camera mode. + * Set the camera to continue looking at the specified position even while the camera moves. Only works if the + * camera is in independent mode. * @function Camera.keepLookingAt - * @param {Vec3} position Position to keep looking at. + * @param {Vec3} position - Position to keep looking at. */ void keepLookingAt(const glm::vec3& position); /**jsdoc - * Stops the camera from continually looking at a position that was set with - * `keepLookingAt` + * Stops the camera from continually looking at the position that was set with Camera.keepLookingAt. * @function Camera.stopLookingAt */ void stopLooking() { _isKeepLookingAt = false; } signals: /**jsdoc - * Triggered when camera mode has changed. + * Triggered when the camera mode changes. * @function Camera.modeUpdated * @return {Signal} + * @example + * function onCameraModeUpdated(newMode) { + * print("The camera mode has changed to " + newMode); + * } + * + * Camera.modeUpdated.connect(onCameraModeUpdated); */ void modeUpdated(const QString& newMode); diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c15f01efe9..9818f70f6a 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -24,6 +24,7 @@ exports.handlers = { '../../libraries/entities/src', '../../libraries/networking/src', '../../libraries/shared/src', + '../../libraries/shared/src/shared', '../../libraries/script-engine/src', ]; var exts = ['.h', '.cpp']; From 9d9f684af5a54c9922aeec2214461d106a0d64ca Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 12 Nov 2017 11:44:51 +1300 Subject: [PATCH 022/137] Tidying --- .../scripting/ClipboardScriptingInterface.h | 26 ++++---- .../src/scripting/MenuScriptingInterface.h | 63 ++++++++++--------- .../script-engine/src/MenuItemProperties.cpp | 20 +++--- libraries/shared/src/shared/Camera.h | 10 +-- 4 files changed, 60 insertions(+), 59 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 0c6119009a..cce300e831 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -38,7 +38,7 @@ public: /**jsdoc * Compute the largest dimension of the extents of the contents held in the clipboard. * @function Clipboard.getClipboardContentsLargestDimension - * @returns {Number} The largest dimension computed. + * @returns {number} The largest dimension computed. */ Q_INVOKABLE float getClipboardContentsLargestDimension(); @@ -46,29 +46,29 @@ public: * Import entities from a JSON file containing entity data into the clipboard. * You can generate a JSON file using {@link Clipboard.exportEntities}. * @function Clipboard.importEntities - * @param {String} filename Path and name of file to import. - * @returns {Boolean} true if the import was successful, otherwise false. + * @param {string} filename Path and name of file to import. + * @returns {boolean} true if the import was successful, otherwise false. */ Q_INVOKABLE bool importEntities(const QString& filename); /**jsdoc * Export the entities specified to a JSON file. * @function Clipboard.exportEntities - * @param {String} filename Path and name of the file to export the entities to. Should have the extension ".json". - * @param {EntityID[]} entityIDs Array of IDs of the entities to export. - * @returns {Boolean} true if the export was successful, otherwise false. + * @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json". + * @param {Uuid[]} entityIDs Array of IDs of the entities to export. + * @returns {boolean} true if the export was successful, otherwise false. */ Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); /**jsdoc * Export the entities with centers within a cube to a JSON file. * @function Clipboard.exportEntities - * @param {String} filename Path and name of the file to export the entities to. Should have the extension ".json". - * @param {Number} x X-coordinate of the cube center. - * @param {Number} y Y-coordinate of the cube center. - * @param {Number} z Z-coordinate of the cube center. - * @param {Number} scale Half dimension of the cube. - * @returns {Boolean} true if the export was successful, otherwise false. + * @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json". + * @param {number} x X-coordinate of the cube center. + * @param {number} y Y-coordinate of the cube center. + * @param {number} z Z-coordinate of the cube center. + * @param {number} scale Half dimension of the cube. + * @returns {boolean} true if the export was successful, otherwise false. */ Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale); @@ -76,7 +76,7 @@ public: * Paste the contents of the clipboard into the world. * @function Clipboard.pasteEntities * @param {Vec3} position Position to paste the clipboard contents at. - * @returns {EntityID[]} Array of entity IDs for the new entities that were created as a result of the paste operation. + * @returns {Uuid[]} Array of entity IDs for the new entities that were created as a result of the paste operation. */ Q_INVOKABLE QVector pasteEntities(glm::vec3 position); }; diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index d1e6337e3f..ab89bae1ca 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -21,6 +21,8 @@ class MenuItemProperties; * The Menu API provides access to the menu that is displayed at the top of the window * on a user's desktop and in the tablet when the "MENU" button is pressed. * + *

+ * *

Groupings

* * A "grouping" provides a way to group a set of menus or menu items together so @@ -39,9 +41,8 @@ class MenuItemProperties; * menuItemName: "Demo Item" * }); * - * function onMenuItemEvent(menuItem) - * { - * if(menuItem == "Demo Item") { + * function onMenuItemEvent(menuItem) { + * if(menuItem === "Demo Item") { * print("Nice! You clicked the demo menu item."); * } * } @@ -73,8 +74,8 @@ public slots: /**jsdoc * Add a new top-level menu. * @function Menu.addMenu - * @param {String} menuName Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. - * @param {String} [grouping] Name of the grouping, if any, to add this menu to. + * @param {string} menuName Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. + * @param {string} [grouping] Name of the grouping, if any, to add this menu to. * @example * Menu.addMenu("Test Menu"); * Menu.addMenu("Test Menu > Test Sub Menu"); @@ -84,7 +85,7 @@ public slots: /**jsdoc * Remove a top-level menu. * @function Menu.removeMenu - * @param {String} menuName Name of the menu to remove. + * @param {string} menuName Name of the menu to remove. * @example * Menu.removeMenu("Test Menu > Test Sub Menu"); * Menu.removeMenu("Test Menu"); @@ -94,8 +95,8 @@ public slots: /**jsdoc * Check whether a top-level menu exists. * @function Menu.menuExists - * @param {String} menuName Name of the menu to check for existence. - * @returns {Boolean} true if the menu exists, otherwise false. + * @param {string} menuName - Name of the menu to check for existence. + * @returns {boolean} true if the menu exists, otherwise false. * @example * if (Menu.menuExists("Developer")) { * // Perform a task if there is a Developer menu. @@ -107,8 +108,8 @@ public slots: * Add a separator with an unclickable label below it. * The line will be placed at the bottom of the menu. * @function Menu.addSeparator - * @param {String} menuName Name of the menu to add a separator to. - * @param {String} separatorName Name of the separator that will be displayed as the label below the separator line. + * @param {string} menuName Name of the menu to add a separator to. + * @param {string} separatorName Name of the separator that will be displayed as the label below the separator line. * @example * Menu.addSeparator("Developer","Test Separator"); */ @@ -117,8 +118,8 @@ public slots: /**jsdoc * Remove a separator from a menu. * @function Menu.removeSeparator - * @param {String} menuName Name of the menu to remove the separator from. - * @param {String} separatorName Name of the separator to remove. + * @param {string} menuName Name of the menu to remove the separator from. + * @param {string} separatorName Name of the separator to remove. * @example * Menu.removeSeparator("Developer","Test Separator"); */ @@ -142,9 +143,9 @@ public slots: /**jsdoc * Add a new menu item to a menu. * @function Menu.addMenuItem - * @param {String} menuName Name of the menu to add a menu item to. - * @param {String} menuItem Name of the menu item. This is what will be displayed in the menu. - * @param {String} [shortcutKey] A shortcut key that can be used to trigger the menu item. + * @param {string} menuName Name of the menu to add a menu item to. + * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. + * @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item. * @example * Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T"); */ @@ -154,8 +155,8 @@ public slots: /**jsdoc * Remove a menu item from a menu. * @function Menu.removeMenuItem - * @param {String} menuName Name of the menu to remove a menu item from. - * @param {String} menuItem Name of the menu item to remove. + * @param {string} menuName Name of the menu to remove a menu item from. + * @param {string} menuItem Name of the menu item to remove. * Menu.removeMenuItem("Developer", "Test"); */ void removeMenuItem(const QString& menuName, const QString& menuitem); @@ -163,9 +164,9 @@ public slots: /**jsdoc * Check if a menu item exists. * @function Menu.menuItemExists - * @param {String} menuName Name of the menu that the menu item is in. - * @param {String} menuItem Name of the menu item to check for existence of. - * @returns {Boolean} true if the menu item exists, otherwise false. + * @param {string} menuName Name of the menu that the menu item is in. + * @param {string} menuItem Name of the menu item to check for existence of. + * @returns {boolean} true if the menu item exists, otherwise false. * @example * if (Menu.menuItemExists("Developer", "Stats")) { * // Perform a task if there is a Stats item in the Developer menu. @@ -183,23 +184,23 @@ public slots: /**jsdoc * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked - * @param {String} menuOption The name of the menu item. - * @returns {Boolean} true if the option is checked, otherwise false. + * @param {string} menuOption The name of the menu item. + * @returns {boolean} true if the option is checked, otherwise false. */ bool isOptionChecked(const QString& menuOption); /**jsdoc * Set a checkable menu item as checked or unchecked. * @function Menu.setIsOptionChecked - * @param {String} menuOption The name of the menu item to modify. - * @param {Boolean} isChecked If true, the menu item will be checked, otherwise it will not be checked. + * @param {string} menuOption The name of the menu item to modify. + * @param {boolean} isChecked If true, the menu item will be checked, otherwise it will not be checked. */ void setIsOptionChecked(const QString& menuOption, bool isChecked); /**jsdoc * Trigger the menu item as if the user clicked on it. * @function Menu.triggerOption - * @param {String} menuOption The name of the menu item to trigger. + * @param {string} menuOption The name of the menu item to trigger. */ void triggerOption(const QString& menuOption); @@ -207,16 +208,16 @@ public slots: * Check whether a menu is enabled. If a menu is disabled it is grayed out and unusable. * Menus are enabled by default. * @function Menu.isMenuEnabled - * @param {String} menuName The name of the menu to check. - * @returns {Boolean} true if the menu is enabled, otherwise false. + * @param {string} menuName The name of the menu to check. + * @returns {boolean} true if the menu is enabled, otherwise false. */ bool isMenuEnabled(const QString& menuName); /**jsdoc * Set a menu to be enabled or disabled. If a menu is disabled it is grayed out and unusable. * @function Menu.setMenuEnabled - * @param {String} menuName The name of the menu to modify. - * @param {Boolean} isEnabled If true, the menu will be enabled, otherwise it will be disabled. + * @param {string} menuName The name of the menu to modify. + * @param {boolean} isEnabled If true, the menu will be enabled, otherwise it will be disabled. */ void setMenuEnabled(const QString& menuName, bool isEnabled); @@ -228,9 +229,9 @@ public slots: signals: /**jsdoc - * Notifies scripts when a menu item is clicked (or triggered by {@link Menu.triggerOption}). + * Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}). * @function Menu.menuItemEvent - * @param {String} menuItem Name of the menu item that was clicked. + * @param {string} menuItem Name of the menu item that was clicked. * @returns {Signal} * @example * function onMenuItemEvent(menuItem) { diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index 5caef7933c..798fe27b79 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -57,18 +57,18 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * the menu item will be placed at the end of the menu. * * @typedef {Object} Menu.MenuItemProperties - * @property {String} menuName Name of the menu. Nested menus can be described using the ">" symbol. - * @property {String} menuItemName Name of the menu item. - * @property {Boolean} [isCheckable=false] Whether or not the menu item is checkable. - * @property {Boolean} [isChecked=false] Whether or not the menu item is checked. - * @property {Boolean} [isSeparator=false] Whether or not the menu item is a separator. - * @property {String} [shortcutKey] A shortcut key that triggers the menu item. + * @property {string} menuName Name of the menu. Nested menus can be described using the ">" symbol. + * @property {string} menuItemName Name of the menu item. + * @property {boolean} [isCheckable=false] Whether or not the menu item is checkable. + * @property {boolean} [isChecked=false] Whether or not the menu item is checked. + * @property {boolean} [isSeparator=false] Whether or not the menu item is a separator. + * @property {string} [shortcutKey] A shortcut key that triggers the menu item. * @property {KeyEvent} [shortcutKeyEvent] A {@link KeyEvent} that specifies a key that triggers the menu item. - * @property {Number} [position] The position to place the new menu item. An integer number with 0 being the first + * @property {number} [position] The position to place the new menu item. An integer number with 0 being the first * menu item. - * @property {String} [beforeItem] The name of the menu item to place this menu item before. - * @property {String} [afterItem] The name of the menu item to place this menu item after. - * @property {String} [grouping] The name of grouping to add this menu item to. + * @property {string} [beforeItem] The name of the menu item to place this menu item before. + * @property {string} [afterItem] The name of the menu item to place this menu item after. + * @property {string} [grouping] The name of grouping to add this menu item to. */ void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) { properties.menuName = object.property("menuName").toVariant().toString(); diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index ae096256fc..a74127b813 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -95,7 +95,7 @@ class Camera : public QObject { * @property position {Vec3} The position of the camera. You can set this value only when the camera is in entity mode. * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in entity * mode. - * @property mode {String} The camera mode. + * @property mode {string} The camera mode. * @property frustum {Frustum} The camera frustum. * @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera * is in entity mode. @@ -130,14 +130,14 @@ public slots: /**jsdoc * Get the current camera mode. You can also get the mode using the Camera.mode property. * @function Camera.getModeString - * @return {String} The current camera mode. + * @return {string} The current camera mode. */ QString getModeString() const; /**jsdoc * Set the camera mode. You can also set the mode using the Camera.mode property. * @function Camera.setModeString - * @param {String} mode - The mode to set the camera to. + * @param {string} mode - The mode to set the camera to. */ void setModeString(const QString& mode); @@ -175,8 +175,8 @@ public slots: * Compute a {PickRay} based on the current camera configuration and the specified x, y position on the screen. * The {PickRay} can be used in functions such as {Entities.findRayIntersection} and {Overlays.findRayIntersection}. * @function Camera.computePickRay - * @param {Number} x - X-coordinate on screen. - * @param {Number} y - Y-coordinate on screen. + * @param {number} x - X-coordinate on screen. + * @param {number} y - Y-coordinate on screen. * @return {PickRay} The computed {PickRay}. * @example * function onMousePressEvent(event) { From a2ece7a3adf0d2b2c8d567000d06ba073679fa89 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 12 Nov 2017 12:02:58 +1300 Subject: [PATCH 023/137] Initial, placeholder JSDoc for Uuid --- libraries/script-engine/src/ScriptUUID.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index 3f504c35ba..5b5726ea83 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -17,6 +17,13 @@ #include #include +/**jsdoc +* A UUID. Represented in JavaScript as a string in the format, {00000000-0000-0000-0000-000000000000}. +* Used to uniquely identify entities, overlays, avatars, and the like. +* +* @typedef Uuid +*/ + /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API class ScriptUUID : public QObject, protected QScriptable { Q_OBJECT From e4daaa8d6766ebe10aa06e788370cbb84c14c47d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 12 Nov 2017 12:50:39 +1300 Subject: [PATCH 024/137] ViewFrustum JSDoc --- libraries/shared/src/ViewFrustum.h | 14 ++++++++++++-- libraries/shared/src/shared/Camera.h | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index d1b88fb2a5..754f79d99c 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -33,8 +33,18 @@ const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; const float DEFAULT_NEAR_CLIP = 0.08f; const float DEFAULT_FAR_CLIP = 16384.0f; -// the "ViewFrustum" has a "keyhole" shape: a regular frustum for stuff that is "visible" with -// a central sphere for stuff that is nearby (for physics simulation). +/**jsdoc + * A ViewFrustum has a "keyhole" shape: a regular frustum for stuff that is visible plus a central sphere for stuff that is + * nearby (for physics simulation). + * + * @typedef ViewFrustum + * @property {number} aspectRatio - Aspect ratio of the frustum. + * @property {number} centerRadius - Center radius of the keyhole in meters. + * @property {number} fieldOfView - Horizontal field of view in degrees. + * @property {Quat} orientation - The direction that the frustum is looking at. + * @property {Vec3} position - The location of the frustum's apex. + * @property {Mat4} projection - The projection matrix for the view defined by the frustum. + */ class ViewFrustum { public: diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index a74127b813..678bee6455 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -96,7 +96,7 @@ class Camera : public QObject { * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in entity * mode. * @property mode {string} The camera mode. - * @property frustum {Frustum} The camera frustum. + * @property frustum {ViewFrustum} The camera frustum. * @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera * is in entity mode. */ From dba16c265c17c9de6b26e4e2a30e3fd761db5cad Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 12 Nov 2017 13:50:07 +1300 Subject: [PATCH 025/137] PickRay JSDoc --- libraries/shared/src/RegisteredMetaTypes.h | 8 ++++++++ libraries/shared/src/shared/Camera.h | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 7b7d8d8f47..eb790ae314 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -125,6 +125,14 @@ QVector qVectorQUuidFromScriptValue(const QScriptValue& array); QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube); void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube); +/**jsdoc + * A PickRay defines a vector with a starting point. It is used, for example, when finding entities or overlays that lie under a + * mouse click or intersect a laser beam. + * + * @typedef PickRay + * @property {Vec3} origin - The starting position of the PickRay. + * @property {Quat} direction - The direction that the PickRay travels. + */ class PickRay { public: PickRay() : origin(0.0f), direction(0.0f) { } diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 678bee6455..7d4c6db3cf 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -172,12 +172,13 @@ public slots: void setOrientation(const glm::quat& orientation); /**jsdoc - * Compute a {PickRay} based on the current camera configuration and the specified x, y position on the screen. - * The {PickRay} can be used in functions such as {Entities.findRayIntersection} and {Overlays.findRayIntersection}. + * Compute a {@link PickRay} based on the current camera configuration and the specified x, y position on the + * screen. The {@link PickRay} can be used in functions such as {@link Entities.findRayIntersection} and + * {@link Overlays.findRayIntersection}. * @function Camera.computePickRay * @param {number} x - X-coordinate on screen. * @param {number} y - Y-coordinate on screen. - * @return {PickRay} The computed {PickRay}. + * @return {PickRay} The computed {@link PickRay}. * @example * function onMousePressEvent(event) { * var pickRay = Camera.computePickRay(event.x, event.y); From bde3dbc2fd73876b305274cac2bb937514e269b1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Nov 2017 11:25:11 +1300 Subject: [PATCH 026/137] Uuid JSDoc --- libraries/script-engine/src/ScriptUUID.h | 86 ++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index 5b5726ea83..bf828f5646 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -18,11 +18,13 @@ #include /**jsdoc -* A UUID. Represented in JavaScript as a string in the format, {00000000-0000-0000-0000-000000000000}. -* Used to uniquely identify entities, overlays, avatars, and the like. -* -* @typedef Uuid -*/ + * A UUID (Universally Unique IDentitier) is used to uniquely identify entities, overlays, avatars, and the like. It is + * represented in JavaScript as a string in the format, {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}, where the "n"s are + * hexadecimal digits. + * + * @namespace Uuid + * @property NULL {Uuid} The null UUID, {00000000-0000-0000-0000-000000000000}. + */ /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API class ScriptUUID : public QObject, protected QScriptable { @@ -31,10 +33,84 @@ class ScriptUUID : public QObject, protected QScriptable { public slots: QUuid fromString(const QString& string); + /**jsdoc + * Generates a UUID from a string representation of the UUID. + * @function Uuid.fromString + * @param {string} string - A string representation of a UUID. The curly braces are optional. + * @returns {Uuid} A UUID if the given string is valid, null otherwise. + * @example Valid and invalid parameters. + * var uuid = Uuid.fromString("{527c27ea-6d7b-4b47-9ae2-b3051d50d2cd}"); + * print(uuid); // {527c27ea-6d7b-4b47-9ae2-b3051d50d2cd} + * + * uuid = Uuid.fromString("527c27ea-6d7b-4b47-9ae2-b3051d50d2cd"); + * print(uuid); // {527c27ea-6d7b-4b47-9ae2-b3051d50d2cd} + * + * uuid = Uuid.fromString("527c27ea"); + * print(uuid); // null + */ + + /**jsdoc + * Generates a string representation of a UUID. However, because UUIDs are represented in JavaScript as strings, this is in + * effect a no-op. + * @function Uuid.toString + * @param {Uuid} id - The UUID to generate a string from. + * @returns {string} - A string representation of the UUID. + */ QString toString(const QUuid& id); + + /**jsdoc + * Generate a new UUID. + * @function Uuid.generate + * @returns {Uuid} A new UUID. + * @example Generate a new UUID and reports its JavaScript type. + * var uuid = Uuid.generate(); + * print(uuid); // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + * print(typeof uuid); // string + */ QUuid generate(); + + /**jsdoc + * Test whether two given UUIDs are equal. + * @function Uuid.isEqual + * @param {Uuid} idA - The first UUID to compare. + * @param {Uuid} idB - The second UUID to compare. + * @returns {boolean} true if the two UUIDs are equal, otherwise false. + * @example Demonstrate true and false cases. + * var uuidA = Uuid.generate(); + * var uuidB = Uuid.generate(); + * print(Uuid.isEqual(uuidA, uuidB)); // false + * uuidB = uuidA; + * print(Uuid.isEqual(uuidA, uuidB)); // true + */ bool isEqual(const QUuid& idA, const QUuid& idB); + + /**jsdoc + * Test whether a given UUID is null. + * @function Uuid.isNull + * @param {Uuid} id - The UUID to test. + * @returns {boolean} true if the UUID equals Uuid.NULL or is null, otherwise false. + * @example Demonstrate true and false cases. + * var uuid; // undefined + * print(Uuid.isNull(uuid)); // false + * uuid = Uuid.generate(); + * print(Uuid.isNull(uuid)); // false + * uuid = Uuid.NULL; + * print(Uuid.isNull(uuid)); // true + * uuid = null; + * print(Uuid.isNull(uuid)); // true + */ bool isNull(const QUuid& id); + + /**jsdoc + * Print to the program log a text label followed by the UUID value. + * @function Uuid.print + * @param {string} label - The label to print. + * @param {Uuid} id - The UUID to print. + * @example Two ways of printing a label plus UUID. + * var uuid = Uuid.generate(); + * Uuid.print("Generated UUID:", uuid); // Generated UUID: {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + * print("Generated UUID: " + uuid); // Generated UUID: {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + */ void print(const QString& label, const QUuid& id); private: From 2119e01ac37feb9b4da8cf0e600c781cf667c819 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Nov 2017 11:59:21 +1300 Subject: [PATCH 027/137] Tidying --- interface/src/FancyCamera.h | 2 +- .../src/scripting/MenuScriptingInterface.h | 35 ++++++++++--------- libraries/shared/src/shared/Camera.h | 16 ++++----- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index d7cf39dd8c..bee21bad22 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -52,7 +52,7 @@ public slots: * entity using the Camera.cameraEntity property. * @function Camera.setCameraEntity * @param {Uuid} entityID The entity that the camera should follow when it's in entity mode. - * @example + * @example Move your camera to the position and orientation of the closest entity. * Camera.setModeString("entity"); * var entity = Entities.findClosestEntity(MyAvatar.position, 100.0); * Camera.setCameraEntity(entity); diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index ab89bae1ca..b5a11581cb 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -33,7 +33,7 @@ class MenuItemProperties; * * @namespace Menu * - * @example + * @example Add a menu and menu item, and detect clicks on the menu item added. * Menu.addMenu("Demo Menu"); * * Menu.addMenuItem({ @@ -76,7 +76,7 @@ public slots: * @function Menu.addMenu * @param {string} menuName Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. * @param {string} [grouping] Name of the grouping, if any, to add this menu to. - * @example + * @example Add a menu and a nested submenu. * Menu.addMenu("Test Menu"); * Menu.addMenu("Test Menu > Test Sub Menu"); */ @@ -86,7 +86,7 @@ public slots: * Remove a top-level menu. * @function Menu.removeMenu * @param {string} menuName Name of the menu to remove. - * @example + * @example Remove a menu and nested submenu. * Menu.removeMenu("Test Menu > Test Sub Menu"); * Menu.removeMenu("Test Menu"); */ @@ -97,20 +97,21 @@ public slots: * @function Menu.menuExists * @param {string} menuName - Name of the menu to check for existence. * @returns {boolean} true if the menu exists, otherwise false. - * @example + * @example Check if the "Developer" menu exists. * if (Menu.menuExists("Developer")) { - * // Perform a task if there is a Developer menu. + * print("Developer menu exists."); * } */ bool menuExists(const QString& menuName); /**jsdoc - * Add a separator with an unclickable label below it. - * The line will be placed at the bottom of the menu. + * Add a separator with an unclickable label below it. The separator will be placed at the bottom of the menu. + * If you want to add a separator at a specific point in the menu, use {@link Menu.addMenuItem} with + * {@link Menu.MenuItemProperties} instead. * @function Menu.addSeparator * @param {string} menuName Name of the menu to add a separator to. * @param {string} separatorName Name of the separator that will be displayed as the label below the separator line. - * @example + * @example Add a separator. * Menu.addSeparator("Developer","Test Separator"); */ void addSeparator(const QString& menuName, const QString& separatorName); @@ -120,7 +121,7 @@ public slots: * @function Menu.removeSeparator * @param {string} menuName Name of the menu to remove the separator from. * @param {string} separatorName Name of the separator to remove. - * @example + * @example Remove a separator. * Menu.removeSeparator("Developer","Test Separator"); */ void removeSeparator(const QString& menuName, const QString& separatorName); @@ -129,7 +130,7 @@ public slots: * Add a new menu item to a menu. * @function Menu.addMenuItem * @param {Menu.MenuItemProperties} properties Properties of the menu item to create. - * @example + * @example Add a menu item using {@link Menu.MenuItemProperties}. * Menu.addMenuItem({ * menuName: "Developer", * menuItemName: "Test", @@ -141,12 +142,12 @@ public slots: void addMenuItem(const MenuItemProperties& properties); /**jsdoc - * Add a new menu item to a menu. + * Add a new menu item to a menu. The new item is added at the end of the menu. * @function Menu.addMenuItem * @param {string} menuName Name of the menu to add a menu item to. * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. * @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item. - * @example + * @example Add a menu item to the end of the "Developer" menu. * Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T"); */ void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey); @@ -167,9 +168,9 @@ public slots: * @param {string} menuName Name of the menu that the menu item is in. * @param {string} menuItem Name of the menu item to check for existence of. * @returns {boolean} true if the menu item exists, otherwise false. - * @example + * @example Determine if the Developer > Stats menu exists. * if (Menu.menuItemExists("Developer", "Stats")) { - * // Perform a task if there is a Stats item in the Developer menu. + * print("Developer > Stats menu item exists."); * } */ bool menuItemExists(const QString& menuName, const QString& menuitem); @@ -185,7 +186,7 @@ public slots: * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked * @param {string} menuOption The name of the menu item. - * @returns {boolean} true if the option is checked, otherwise false. + * @returns {boolean} true if the option is checked, otherwise false. */ bool isOptionChecked(const QString& menuOption); @@ -209,7 +210,7 @@ public slots: * Menus are enabled by default. * @function Menu.isMenuEnabled * @param {string} menuName The name of the menu to check. - * @returns {boolean} true if the menu is enabled, otherwise false. + * @returns {boolean} true if the menu is enabled, otherwise false. */ bool isMenuEnabled(const QString& menuName); @@ -233,7 +234,7 @@ signals: * @function Menu.menuItemEvent * @param {string} menuItem Name of the menu item that was clicked. * @returns {Signal} - * @example + * @example Detect menu item events. * function onMenuItemEvent(menuItem) { * print("You clicked on " + menuItem); * } diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 7d4c6db3cf..83f1174244 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -130,7 +130,7 @@ public slots: /**jsdoc * Get the current camera mode. You can also get the mode using the Camera.mode property. * @function Camera.getModeString - * @return {string} The current camera mode. + * @returns {string} The current camera mode. */ QString getModeString() const; @@ -144,7 +144,7 @@ public slots: /**jsdoc * Get the current camera position. You can also get the position using the Camera.position property. * @function Camera.getPosition - * @return {Vec3} The current camera position. + * @returns {Vec3} The current camera position. */ glm::vec3 getPosition() const { return _position; } @@ -159,7 +159,7 @@ public slots: /**jsdoc * Get the current camera orientation. You can also get the orientation using the Camera.orientation property. * @function Camera.getOrientation - * @return {Quat} The current camera orientation. + * @returns {Quat} The current camera orientation. */ glm::quat getOrientation() const { return _orientation; } @@ -178,8 +178,8 @@ public slots: * @function Camera.computePickRay * @param {number} x - X-coordinate on screen. * @param {number} y - Y-coordinate on screen. - * @return {PickRay} The computed {@link PickRay}. - * @example + * @returns {PickRay} The computed {@link PickRay}. + * @example Use a PickRay to detect mouse clicks on entities. * function onMousePressEvent(event) { * var pickRay = Camera.computePickRay(event.x, event.y); * var intersection = Entities.findRayIntersection(pickRay); @@ -196,7 +196,7 @@ public slots: * Rotate the camera to look at the specified position. Only works if the camera is in independent mode. * @function Camera.lookAt * @param {Vec3} position - Position to look at. - * @example + * @example Rotate your camera to look at entities as you click on them with your mouse. * function onMousePressEvent(event) { * var pickRay = Camera.computePickRay(event.x, event.y); * var intersection = Entities.findRayIntersection(pickRay); @@ -231,8 +231,8 @@ signals: /**jsdoc * Triggered when the camera mode changes. * @function Camera.modeUpdated - * @return {Signal} - * @example + * @returns {Signal} + * @example Report camera mode changes. * function onCameraModeUpdated(newMode) { * print("The camera mode has changed to " + newMode); * } From 9c5149733d7e4a623e50f1f19b8e998ea1af73f3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Nov 2017 12:41:28 +1300 Subject: [PATCH 028/137] Paths JSDoc --- libraries/shared/src/PathUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 8c4bcf2394..a59e0f39ab 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -21,7 +21,8 @@ /**jsdoc * @namespace Paths * @readonly - * @property {string} resources The path to the resources directory. + * @property {string} resources The path to the resources directory. Readonly + * @property {string} defaultScripts The path to the defaultScripts directory. Readonly */ class PathUtils : public QObject, public Dependency { Q_OBJECT From 70847a95135a4d5a09166695713c0e9447c29665 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 08:49:45 +0100 Subject: [PATCH 029/137] Wrong texture map assigned to cascade slots --- libraries/render-utils/src/LightStage.cpp | 3 +++ libraries/render-utils/src/Shadow.slh | 30 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index c3a4ebf137..0699838836 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -46,9 +46,12 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { } LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { + cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); + Schema schema; _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); _cascades.resize(cascadeCount); + _schemaBuffer.edit().cascadeCount = cascadeCount; } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 46661b3b8a..8196decd6d 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -20,6 +20,14 @@ uniform shadowTransformBuffer { ShadowParameters shadow; }; +int getShadowCascadeCount() { + return shadow.cascadeCount; +} + +float getShadowCascadeMinDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].minDistance; +} + mat4 getShadowReprojection(int cascadeIndex) { return shadow.cascades[cascadeIndex].reprojection; } @@ -76,12 +84,30 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); } float evalShadowAttenuation(vec4 position) { - return evalShadowCascadeAttenuation(0, position); + // Cascade selection based on : + // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx + vec4 currentPixelDepth = position.zzzz; + vec4 cascadeDepthLimits = vec4( + getShadowCascadeMinDistance(0), + getShadowCascadeMinDistance(1), + getShadowCascadeMinDistance(2), + getShadowCascadeMinDistance(3) + ); + vec4 comparison = vec4( currentPixelDepth > cascadeDepthLimits); + int cascadeCount = getShadowCascadeCount(); + float fIndex = dot( + vec4( cascadeCount > 0, cascadeCount > 1, cascadeCount > 2, cascadeCount > 3), + comparison + ); + + fIndex = min( fIndex, cascadeCount-1 ); + int currentCascadeIndex = int(fIndex); + + return evalShadowCascadeAttenuation(currentCascadeIndex, position); } <@endif@> From dd0eaafc0da40fea29c1cd08139ec6c30da94ea5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 12:33:46 +0100 Subject: [PATCH 030/137] Screen space dithering for PCF with some noise added --- libraries/render-utils/src/Shadow.slh | 29 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 8abbc1de3e..3021ea87f9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -57,17 +57,32 @@ vec2 PCFkernel[4] = vec2[4]( vec2(0.5, -1.5) ); -float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { +float evalShadowNoise(vec4 seed) { + float dot_product = dot(seed, vec4(12.9898,78.233,45.164,94.673)); + return fract(sin(dot_product) * 43758.5453); +} + +float evalShadowAttenuationPCF(vec4 shadowTexcoord) { float shadowScale = getShadowScale(); + // Pattern dithering in screen space // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html - vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); + ivec2 coords = ivec2(gl_FragCoord.xy); +#if 1 + // Add some noise to break dithering + int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4; + coords.x += index & 1; + coords.y += (index & 2) >> 1; +#endif + ivec2 offset = coords & ivec2(1,1); + offset.y = (offset.x+offset.y) & 1; + vec2 offsetF = vec2(offset); float shadowAttenuation = (0.25 * ( - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[0], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[1], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[2], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[3], 0.0)) )); return shadowAttenuation; @@ -82,7 +97,7 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(position, shadowTexcoord); + return evalShadowAttenuationPCF(shadowTexcoord); } <@endif@> From 6611b28e2df468d8a8bd3cb8622a778f1060db5e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 13:54:51 +0100 Subject: [PATCH 031/137] Switched to world space dithering for more visual stability --- libraries/render-utils/src/LightStage.cpp | 2 ++ libraries/render-utils/src/Shadow.slh | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index eb712b075c..92c1e3211b 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -13,6 +13,8 @@ #include "LightStage.h" +#include + std::string LightStage::_stageName { "LIGHT_STAGE"}; const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.0, 0.0, 0.0, diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 3021ea87f9..ceea4d1d32 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -62,18 +62,22 @@ float evalShadowNoise(vec4 seed) { return fract(sin(dot_product) * 43758.5453); } -float evalShadowAttenuationPCF(vec4 shadowTexcoord) { +float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); +#if 0 // Pattern dithering in screen space - // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 coords = ivec2(gl_FragCoord.xy); -#if 1 +#else + // Pattern dithering in world space (mm resolution) + ivec2 coords = ivec2(position.x, position.y+position.z); +#endif // Add some noise to break dithering int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4; coords.x += index & 1; coords.y += (index & 2) >> 1; -#endif + + // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); offset.y = (offset.x+offset.y) & 1; vec2 offsetF = vec2(offset); @@ -97,7 +101,7 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(shadowTexcoord); + return evalShadowAttenuationPCF(position, shadowTexcoord); } <@endif@> From 28d46dc4a5530698c763c886c5eec745b6d1ca45 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 15:56:11 +0100 Subject: [PATCH 032/137] First alpha version of cascade selection code in shader --- libraries/render-utils/src/Shadow.slh | 52 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index e4b41bf60f..95d7b5193e 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -65,8 +65,13 @@ float evalShadowNoise(vec4 seed) { return fract(sin(dot_product) * 43758.5453); } -float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexcoord) { +struct ShadowSampleOffsets { + vec3 points[4]; +}; + +ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { float shadowScale = getShadowScale(); + ShadowSampleOffsets offsets; #if 0 // Pattern dithering in screen space @@ -83,19 +88,28 @@ float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexco // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); offset.y = (offset.x+offset.y) & 1; - vec2 offsetF = vec2(offset); - float shadowAttenuation = (0.25 * ( - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) - )); + offsets.points[0] = shadowScale * vec3(offset + PCFkernel[0], 0.0); + offsets.points[1] = shadowScale * vec3(offset + PCFkernel[1], 0.0); + offsets.points[2] = shadowScale * vec3(offset + PCFkernel[2], 0.0); + offsets.points[3] = shadowScale * vec3(offset + PCFkernel[3], 0.0); + + return offsets; +} + +float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { + + float shadowAttenuation = 0.25 * ( + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3]) + ); return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { +float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSampleOffsets offsets) { vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || @@ -103,10 +117,12 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); + return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } float evalShadowAttenuation(vec4 position) { + ShadowSampleOffsets offsets = evalShadowFilterOffsets(position); + // Cascade selection based on : // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx vec4 currentPixelDepth = position.zzzz; @@ -116,17 +132,13 @@ float evalShadowAttenuation(vec4 position) { getShadowCascadeMinDistance(2), getShadowCascadeMinDistance(3) ); - vec4 comparison = vec4( currentPixelDepth > cascadeDepthLimits); + bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); int cascadeCount = getShadowCascadeCount(); - float fIndex = dot( - vec4( cascadeCount > 0, cascadeCount > 1, cascadeCount > 2, cascadeCount > 3), - comparison - ); - - fIndex = min( fIndex, cascadeCount-1 ); - int currentCascadeIndex = int(fIndex); - - return evalShadowCascadeAttenuation(currentCascadeIndex, position); + bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); + int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); + cascadeIndex = min( cascadeIndex, cascadeCount-1 ); + + return evalShadowCascadeAttenuation(cascadeIndex, position, offsets); } <@endif@> From ac0e816f8cc4f68ca79f1dd8c5c816c74d2deae1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 18:42:34 +0100 Subject: [PATCH 033/137] Cascade selection working on shadow but not in Luci debug mode --- .../gpu-gl/src/gpu/gl/GLBackendShader.cpp | 5 +- .../render-utils/src/DebugDeferredBuffer.cpp | 43 ++++++++-- .../render-utils/src/DebugDeferredBuffer.h | 9 +- libraries/render-utils/src/LightStage.cpp | 2 + libraries/render-utils/src/LightStage.h | 4 +- .../render-utils/src/RenderShadowTask.cpp | 5 +- libraries/render-utils/src/Shadow.slh | 69 ++++----------- libraries/render-utils/src/ShadowCore.slh | 86 +++++++++++++++++++ .../src/debug_deferred_buffer.slf | 3 +- .../src/directional_ambient_light_shadow.slf | 5 +- .../src/directional_skybox_light_shadow.slf | 5 +- .../utilities/render/deferredLighting.qml | 1 + 12 files changed, 162 insertions(+), 75 deletions(-) create mode 100644 libraries/render-utils/src/ShadowCore.slh diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp index 0c1b6880cb..9adfd550ef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp @@ -318,7 +318,10 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot if (requestedBinding != slotBindings.end()) { if (binding != (*requestedBinding)._location) { binding = (*requestedBinding)._location; - glProgramUniform1i(glprogram, location, binding); + for (auto i = 0; i < size; i++) { + // If we are working with an array of textures, reserve for each elemet + glProgramUniform1i(glprogram, location+i, binding+i); + } } } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 1a95329724..55c945549c 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -126,12 +126,14 @@ static const std::string DEFAULT_DEPTH_SHADER { " return vec4(vec3(texture(depthMap, uv).x), 1.0);" " }" }; + static const std::string DEFAULT_LIGHTING_SHADER { "vec4 getFragmentColor() {" " return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" " }" }; -static const std::string DEFAULT_SHADOW_SHADER { + +static const std::string DEFAULT_SHADOW_SHADER{ "uniform sampler2DShadow shadowMap;" "vec4 getFragmentColor() {" " for (int i = 255; i >= 0; --i) {" @@ -144,6 +146,25 @@ static const std::string DEFAULT_SHADOW_SHADER { " }" }; +static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ + "vec3 cascadeColors[4] = vec3[4]( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(0,0,0) );" + "vec4 getFragmentColor() {" + " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" + " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" + " vec4 viewPosition = vec4(frag.position.xyz, 1.0);" + " vec4 worldPosition = getViewInverse() * viewPosition;" + " vec4 cascadeShadowCoords[4] = vec4[4](" + " evalShadowTexcoord(0, worldPosition)," + " evalShadowTexcoord(1, worldPosition)," + " evalShadowTexcoord(2, worldPosition)," + " evalShadowTexcoord(3, worldPosition)" + " );" + " ivec2 cascadeIndices;" + " float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);" + " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" + " }" +}; + static const std::string DEFAULT_LINEAR_DEPTH_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);" @@ -284,11 +305,13 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_SCATTERING_SHADER; case LightingMode: return DEFAULT_LIGHTING_SHADER; - case Shadow0Mode: - case Shadow1Mode: - case Shadow2Mode: - case Shadow3Mode: + case ShadowCascade0Mode: + case ShadowCascade1Mode: + case ShadowCascade2Mode: + case ShadowCascade3Mode: return DEFAULT_SHADOW_SHADER; + case ShadowCascadeIndicesMode: + return DEFAULT_SHADOW_CASCADE_SHADER; case LinearDepthMode: return DEFAULT_LINEAR_DEPTH_SHADER; case HalfLinearDepthMode: @@ -424,8 +447,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I // TODO REMOVE: Temporary until UI auto first = _customPipelines.begin()->first; - - batch.setPipeline(getPipeline(_mode, first)); + auto pipeline = getPipeline(_mode, first); + batch.setPipeline(pipeline); if (deferredFramebuffer) { batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture()); @@ -441,8 +464,12 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - const auto cascadeIndex = glm::clamp(_mode - Mode::Shadow0Mode, 0, (int)globalShadow->getCascadeCount() - 1); + const auto cascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)globalShadow->getCascadeCount() - 1); + const auto shadowBufferLoc = pipeline->getProgram()->getUniformBuffers().findLocation("shadowTransformBuffer"); batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map); + if (shadowBufferLoc >= 0) { + batch.setUniformBuffer(shadowBufferLoc, globalShadow->getBuffer()); + } } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 49e53c4617..104b161720 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -64,10 +64,11 @@ protected: LightmapMode, ScatteringMode, LightingMode, - Shadow0Mode, - Shadow1Mode, - Shadow2Mode, - Shadow3Mode, + ShadowCascade0Mode, + ShadowCascade1Mode, + ShadowCascade2Mode, + ShadowCascade3Mode, + ShadowCascadeIndicesMode, LinearDepthMode, HalfLinearDepthMode, HalfNormalMode, diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index bfc4805413..1820076545 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -126,6 +126,8 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); schemaCascade.minDistance = viewMinShadowDistance; schemaCascade.maxDistance = viewMaxShadowDistance; + cascade.minDistance = viewMinShadowDistance; + cascade.maxDistance = viewMaxShadowDistance; } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 60c4a49678..07ef0c25b6 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -56,6 +56,8 @@ public: gpu::FramebufferPointer framebuffer; gpu::TexturePointer map; + float minDistance; + float maxDistance; const std::shared_ptr& getFrustum() const { return _frustum; } @@ -95,8 +97,6 @@ public: }; UniformBufferView _schemaBuffer = nullptr; - - void setupCascades(); friend class Light; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d9af9659e5..0c4e5d4a61 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -253,8 +253,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O output.edit0() = args->_renderMode; const auto globalShadow = lightStage->getCurrentKeyShadow(); - const auto globalShadowCascadeCount = globalShadow->getCascadeCount(); - if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); const auto nearClip = args->getViewFrustum().getNearClip(); @@ -263,7 +262,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O static const float SHADOW_MAX_DISTANCE = 25.0f; static const float SHADOW_OVERLAP_DISTANCE = 1.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadowCascadeCount - 1 - _cascadeIndex); + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; if (_cascadeIndex == 0) { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 95d7b5193e..18a39782b6 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,43 +11,11 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> -<@include Shadows_shared.slh@> +<@include ShadowCore.slh@> // the shadow texture uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; -uniform shadowTransformBuffer { - ShadowParameters shadow; -}; - -int getShadowCascadeCount() { - return shadow.cascadeCount; -} - -float getShadowCascadeMinDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].minDistance; -} - -mat4 getShadowReprojection(int cascadeIndex) { - return shadow.cascades[cascadeIndex].reprojection; -} - -float getShadowScale() { - return shadow.invMapSize; -} - -float getShadowBias(int cascadeIndex) { - return shadow.cascades[cascadeIndex].bias; -} - -// Compute the texture coordinates from world coordinates -vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { - float bias = -getShadowBias(cascadeIndex); - - vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); -} - // Sample the shadowMap with PCF (built-in) float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) { return texture(shadowMaps[cascadeIndex], shadowTexcoord); @@ -109,8 +77,7 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSampleOffsets offsets) { - vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { @@ -120,25 +87,23 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSample return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } -float evalShadowAttenuation(vec4 position) { - ShadowSampleOffsets offsets = evalShadowFilterOffsets(position); - - // Cascade selection based on : - // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx - vec4 currentPixelDepth = position.zzzz; - vec4 cascadeDepthLimits = vec4( - getShadowCascadeMinDistance(0), - getShadowCascadeMinDistance(1), - getShadowCascadeMinDistance(2), - getShadowCascadeMinDistance(3) +float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { + ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); + vec4 cascadeShadowCoords[4] = vec4[4] ( + evalShadowTexcoord(0, worldPosition), + evalShadowTexcoord(1, worldPosition), + evalShadowTexcoord(2, worldPosition), + evalShadowTexcoord(3, worldPosition) ); - bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); - int cascadeCount = getShadowCascadeCount(); - bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); - int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); - cascadeIndex = min( cascadeIndex, cascadeCount-1 ); + ivec2 cascadeIndices; + float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices); - return evalShadowCascadeAttenuation(cascadeIndex, position, offsets); + vec2 cascadeAttenuations = vec2(1.0, 1.0); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); + if (cascadeMix > 0.0) { + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); + } + return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); } <@endif@> diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh new file mode 100644 index 0000000000..3e2fc3a8b8 --- /dev/null +++ b/libraries/render-utils/src/ShadowCore.slh @@ -0,0 +1,86 @@ + +<@if not SHADOW_CORE_SLH@> +<@def SHADOW_CORE_SLH@> + +<@include Shadows_shared.slh@> + +uniform shadowTransformBuffer { + ShadowParameters shadow; +}; + +int getShadowCascadeCount() { + return shadow.cascadeCount; +} + +float getShadowCascadeMinDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].minDistance; +} + +mat4 getShadowReprojection(int cascadeIndex) { + return shadow.cascades[cascadeIndex].reprojection; +} + +float getShadowScale() { + return shadow.invMapSize; +} + +float getShadowBias(int cascadeIndex) { + return shadow.cascades[cascadeIndex].bias; +} + +// Compute the texture coordinates from world coordinates +vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { + float bias = -getShadowBias(cascadeIndex); + + vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; + return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); +} + +int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { + int cascadeIndex; + for (cascadeIndex=0 ; cascadeIndex diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index e9750f0054..426de623a1 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -16,7 +16,6 @@ <@include gpu/Color.slh@> <$declareColorWheel()$> - uniform sampler2D linearDepthMap; uniform sampler2D halfLinearDepthMap; uniform sampler2D halfNormalMap; @@ -24,6 +23,8 @@ uniform sampler2D occlusionMap; uniform sampler2D occlusionBlurredMap; uniform sampler2D scatteringMap; +<@include ShadowCore.slh@> + <$declareDeferredCurvature()$> float curvatureAO(float k) { diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index d3778c9228..5f71809249 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -26,8 +26,9 @@ void main(void) { DeferredFrameTransform deferredTransform = getDeferredFrameTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0); - float shadowAttenuation = evalShadowAttenuation(worldPos); + vec4 viewPos = vec4(frag.position.xyz, 1.0); + vec4 worldPos = getViewInverse() * viewPos; + float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); if (frag.mode == FRAG_MODE_UNLIT) { discard; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow.slf b/libraries/render-utils/src/directional_skybox_light_shadow.slf index 3ca0f71df5..5c549df95d 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -26,8 +26,9 @@ void main(void) { DeferredFrameTransform deferredTransform = getDeferredFrameTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0); - float shadowAttenuation = evalShadowAttenuation(worldPos); + vec4 viewPos = vec4(frag.position.xyz, 1.0); + vec4 worldPos = getViewInverse() * viewPos; + float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index def8b1fcd1..1da7871172 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -188,6 +188,7 @@ Rectangle { ListElement { text: "Shadow Cascade 1"; color: "White" } ListElement { text: "Shadow Cascade 2"; color: "White" } ListElement { text: "Shadow Cascade 3"; color: "White" } + ListElement { text: "Shadow Cascade Indices"; color: "White" } ListElement { text: "Linear Depth"; color: "White" } ListElement { text: "Half Linear Depth"; color: "White" } ListElement { text: "Half Normal"; color: "White" } From 842cddde88603469e5791ded31682d244010ba9a Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 13 Nov 2017 11:58:49 -0800 Subject: [PATCH 034/137] Deleted new members of RenderableModelEntity.h that were not necessary to implement the loop,hold,play properties for an entity animationq --- libraries/entities-renderer/src/RenderableModelEntityItem.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 65b915465c..16892ce3e0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -185,11 +185,6 @@ private: bool _animating { false }; uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; - //bool _previousHold{ false }; - float _currentFramePropertyValue{ 0 }; - float _firstFramePropertyValue{ 0 }; - float _lastFramePropertyValue{ 0 }; - bool _stopLoop{ false }; float _endAnim{ 0 }; }; From fb550d59f0f19e0d48aab305f8386fa7738cf698 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 14 Nov 2017 11:34:15 +1300 Subject: [PATCH 035/137] Better place for ViewFrustum JSDoc --- libraries/shared/src/ViewFrustum.h | 13 ------------- libraries/shared/src/shared/Camera.cpp | 12 ++++++++++++ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 28852ac6bd..44d09ad456 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -33,19 +33,6 @@ const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; const float DEFAULT_NEAR_CLIP = 0.08f; const float DEFAULT_FAR_CLIP = 16384.0f; -/**jsdoc - * A ViewFrustum has a "keyhole" shape: a regular frustum for stuff that is visible plus a central sphere for stuff that is - * nearby (for physics simulation). - * - * @typedef ViewFrustum - * @property {number} aspectRatio - Aspect ratio of the frustum. - * @property {number} centerRadius - Center radius of the keyhole in meters. - * @property {number} fieldOfView - Horizontal field of view in degrees. - * @property {Quat} orientation - The direction that the frustum is looking at. - * @property {Vec3} position - The location of the frustum's apex. - * @property {Mat4} projection - The projection matrix for the view defined by the frustum. - */ - class ViewFrustum { public: // setters for camera attributes diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index ab841c4717..f262febbc7 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -131,6 +131,18 @@ void Camera::loadViewFrustum(ViewFrustum& frustum) const { frustum.calculate(); } +/**jsdoc + * A ViewFrustum has a "keyhole" shape: a regular frustum for stuff that is visible plus a central sphere for stuff that is + * nearby (for physics simulation). + * + * @typedef ViewFrustum + * @property {Vec3} position - The location of the frustum's apex. + * @property {Quat} orientation - The direction that the frustum is looking at. + * @property {number} centerRadius - Center radius of the keyhole in meters. + * @property {number} fieldOfView - Horizontal field of view in degrees. + * @property {number} aspectRatio - Aspect ratio of the frustum. + * @property {Mat4} projection - The projection matrix for the view defined by the frustum. + */ QVariantMap Camera::getViewFrustum() { ViewFrustum frustum; loadViewFrustum(frustum); From 010104d42aeb8d82548a9e330ee78c8ac59a4e1b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 10:11:39 +0100 Subject: [PATCH 036/137] Switched off noise and switched back to screen space dither --- libraries/render-utils/src/Shadow.slh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index ceea4d1d32..66d2937deb 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,6 +11,9 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> +#define SHADOW_NOISE_ENABLED 0 +#define SHADOW_SCREEN_SPACE_DITHER 1 + // the shadow texture uniform sampler2DShadow shadowMap; @@ -65,17 +68,20 @@ float evalShadowNoise(vec4 seed) { float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); -#if 0 +#if SHADOW_SCREEN_SPACE_DITHER // Pattern dithering in screen space ivec2 coords = ivec2(gl_FragCoord.xy); #else // Pattern dithering in world space (mm resolution) ivec2 coords = ivec2(position.x, position.y+position.z); #endif + +#if SHADOW_NOISE_ENABLED // Add some noise to break dithering int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4; coords.x += index & 1; coords.y += (index & 2) >> 1; +#endif // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); From 74b0b52edb6877e5d3c288a59d92b2c9741599a0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 16:57:22 +0100 Subject: [PATCH 037/137] Working CSM with smooth blend between cascades. Switched to 4 1024 cascades --- .../render-utils/src/DebugDeferredBuffer.cpp | 23 +++++++++++------- .../render-utils/src/DebugDeferredBuffer.h | 3 ++- libraries/render-utils/src/LightStage.cpp | 7 +++--- libraries/render-utils/src/LightStage.h | 4 +++- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 15 +++++++----- libraries/render-utils/src/Shadow.slh | 14 +++++++---- libraries/render-utils/src/ShadowCore.slh | 24 ++++++++++++------- libraries/render-utils/src/Shadows_shared.slh | 2 +- .../src/directional_ambient_light_shadow.slf | 2 +- .../src/directional_skybox_light_shadow.slf | 2 +- 11 files changed, 62 insertions(+), 36 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 55c945549c..7d316d6e56 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -38,7 +38,7 @@ void DebugDeferredBufferConfig::setMode(int newMode) { emit dirty(); } -enum Slot { +enum TextureSlot { Albedo = 0, Normal, Specular, @@ -55,7 +55,11 @@ enum Slot { AmbientOcclusionBlurred }; - +enum ParamSlot { + CameraCorrection = 0, + DeferredFrameTransform, + ShadowTransform +}; static const std::string DEFAULT_ALBEDO_SHADER { "vec4 getFragmentColor() {" @@ -147,7 +151,7 @@ static const std::string DEFAULT_SHADOW_SHADER{ }; static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ - "vec3 cascadeColors[4] = vec3[4]( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(0,0,0) );" + "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );" "vec4 getFragmentColor() {" " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" @@ -160,7 +164,7 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ " evalShadowTexcoord(3, worldPosition)" " );" " ivec2 cascadeIndices;" - " float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);" + " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" " }" }; @@ -378,6 +382,10 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str const auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("cameraCorrectionBuffer", CameraCorrection)); + slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", DeferredFrameTransform)); + slotBindings.insert(gpu::Shader::Binding("shadowTransformBuffer", ShadowTransform)); + slotBindings.insert(gpu::Shader::Binding("albedoMap", Albedo)); slotBindings.insert(gpu::Shader::Binding("normalMap", Normal)); slotBindings.insert(gpu::Shader::Binding("specularMap", Specular)); @@ -429,6 +437,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto& linearDepthTarget = inputs.get1(); auto& surfaceGeometryFramebuffer = inputs.get2(); auto& ambientOcclusionFramebuffer = inputs.get3(); + auto& frameTransform = inputs.get4(); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -465,11 +474,9 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I const auto& globalShadow = lightAndShadow.second; if (globalShadow) { const auto cascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)globalShadow->getCascadeCount() - 1); - const auto shadowBufferLoc = pipeline->getProgram()->getUniformBuffers().findLocation("shadowTransformBuffer"); batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map); - if (shadowBufferLoc >= 0) { - batch.setUniformBuffer(shadowBufferLoc, globalShadow->getBuffer()); - } + batch.setUniformBuffer(ShadowTransform, globalShadow->getBuffer()); + batch.setUniformBuffer(DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 104b161720..8227c4f7a3 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -15,6 +15,7 @@ #include #include +#include "DeferredFrameTransform.h" #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" #include "AmbientOcclusionEffect.h" @@ -37,7 +38,7 @@ signals: class DebugDeferredBuffer { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = DebugDeferredBufferConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 1820076545..6808c75914 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -21,7 +21,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0 }; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -58,10 +58,11 @@ LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, + float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, float nearDepth, float farDepth) { assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); + assert(viewOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -124,7 +125,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); - schemaCascade.minDistance = viewMinShadowDistance; + schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance; schemaCascade.maxDistance = viewMaxShadowDistance; cascade.minDistance = viewMinShadowDistance; cascade.maxDistance = viewMaxShadowDistance; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 07ef0c25b6..9f395c905a 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -71,7 +71,9 @@ public: Shadow(model::LightPointer light, unsigned int cascadeCount = 1); - void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index ff71d54344..277492b238 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -225,7 +225,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Debugging stages { // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, deferredFrameTransform)); task.addJob("DebugDeferredBuffer", debugFramebuffers); const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 0c4e5d4a61..88b3fe9013 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,17 +260,20 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto farClip = args->getViewFrustum().getFarClip(); const auto nearDepth = -args->_boomOffset.z; - static const float SHADOW_MAX_DISTANCE = 25.0f; - static const float SHADOW_OVERLAP_DISTANCE = 1.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); - float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; + static const float SHADOW_MAX_DISTANCE = 30.0f; + static const float CASCADE_LEVEL_SCALE = 2.0f; + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(CASCADE_LEVEL_SCALE, globalShadow->getCascadeCount() - 1 - _cascadeIndex); + float minCascadeDistance = maxCascadeDistance / CASCADE_LEVEL_SCALE; + float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; if (_cascadeIndex == 0) { minCascadeDistance = nearDepth; - } + } else { + minCascadeDistance -= shadowOverlapDistance; + } minCascadeDistance = std::max(minCascadeDistance, nearDepth); maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 18a39782b6..036f1029e6 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -13,6 +13,9 @@ <@include ShadowCore.slh@> +#define SHADOW_NOISE_ENABLED 0 +#define SHADOW_SCREEN_SPACE_DITHER 1 + // the shadow texture uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; @@ -41,17 +44,20 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { float shadowScale = getShadowScale(); ShadowSampleOffsets offsets; -#if 0 +#if SHADOW_SCREEN_SPACE_DITHER // Pattern dithering in screen space ivec2 coords = ivec2(gl_FragCoord.xy); #else // Pattern dithering in world space (mm resolution) ivec2 coords = ivec2(position.x, position.y+position.z); #endif + +#if SHADOW_NOISE_ENABLED // Add some noise to break dithering int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4; coords.x += index & 1; coords.y += (index & 2) >> 1; +#endif // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); @@ -87,7 +93,7 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } -float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { +float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[4] = vec4[4] ( evalShadowTexcoord(0, worldPosition), @@ -96,11 +102,11 @@ float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { evalShadowTexcoord(3, worldPosition) ); ivec2 cascadeIndices; - float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices); + float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); - if (cascadeMix > 0.0) { + if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); } return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 3e2fc3a8b8..6408ad472a 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -13,7 +13,7 @@ <@include Shadows_shared.slh@> -uniform shadowTransformBuffer { +layout(std140) uniform shadowTransformBuffer { ShadowParameters shadow; }; @@ -21,8 +21,12 @@ int getShadowCascadeCount() { return shadow.cascadeCount; } -float getShadowCascadeMinDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].minDistance; +float getShadowCascadeInvTransitionWidth(int cascadeIndex) { + return shadow.cascades[cascadeIndex].invTransitionWidth; +} + +float getShadowCascadeMaxDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].maxDistance; } mat4 getShadowReprojection(int cascadeIndex) { @@ -58,19 +62,18 @@ int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { return cascadeIndex; } -float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) { +float evalCascadeIndicesAndMix(float viewDepth, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) { + int cascadeCount = getShadowCascadeCount(); #if 0 // Cascade selection based on : // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx - vec4 currentPixelDepth = viewPosition.zzzz; vec4 cascadeDepthLimits = vec4( getShadowCascadeMinDistance(0), getShadowCascadeMinDistance(1), getShadowCascadeMinDistance(2), getShadowCascadeMinDistance(3) ); - bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); - int cascadeCount = getShadowCascadeCount(); + bvec4 comparison = greaterThan( vec4(viewDepth), cascadeDepthLimits); bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); cascadeIndex = min( cascadeIndex, cascadeCount-1 ); @@ -79,8 +82,11 @@ float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], o #endif cascadeIndices.x = cascadeIndex; - cascadeIndices.y = cascadeIndex; - return 1.0; + cascadeIndices.y = cascadeIndex+1; + + float maxDepth = getShadowCascadeMaxDistance(cascadeIndices.x); + float cascadeMixRatio = (maxDepth-viewDepth) * getShadowCascadeInvTransitionWidth(cascadeIndices.x); + return 1.0 - clamp(cascadeMixRatio, 0.0, 1.0); } <@endif@> diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index d5002df9a8..7230ba6134 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,8 +13,8 @@ struct ShadowTransform { MAT4 reprojection; float bias; - float minDistance; float maxDistance; + float invTransitionWidth; float _padding; }; diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index 5f71809249..b791c9297a 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); if (frag.mode == FRAG_MODE_UNLIT) { discard; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow.slf b/libraries/render-utils/src/directional_skybox_light_shadow.slf index 5c549df95d..2bccc68550 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { From d14ebdc0e137341e00c7eed7155def480a5a9b44 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 18:53:50 +0100 Subject: [PATCH 038/137] Adjusted cascade partitions --- .../render-utils/src/DebugDeferredBuffer.cpp | 1 - .../render-utils/src/RenderShadowTask.cpp | 21 ++++++++++++------- libraries/render-utils/src/ShadowCore.slh | 5 ++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 7d316d6e56..eda6c69f88 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -497,7 +497,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I const glm::vec2 topRight(_size.z, _size.w); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId); - batch.setResourceTexture(Albedo, nullptr); batch.setResourceTexture(Normal, nullptr); batch.setResourceTexture(Specular, nullptr); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 88b3fe9013..c140d85560 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,16 +260,23 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto farClip = args->getViewFrustum().getFarClip(); const auto nearDepth = -args->_boomOffset.z; - static const float SHADOW_MAX_DISTANCE = 30.0f; - static const float CASCADE_LEVEL_SCALE = 2.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(CASCADE_LEVEL_SCALE, globalShadow->getCascadeCount() - 1 - _cascadeIndex); - float minCascadeDistance = maxCascadeDistance / CASCADE_LEVEL_SCALE; - float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; + float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; + float minCascadeDistance = nearClip; + float shadowOverlapDistance = 0.0f; + if (globalShadow->getCascadeCount() > 1) { + static const float LOW_CASCADE_MAX_DISTANCE = 2.0f; + const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1)); + + maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex); + minCascadeDistance = maxCascadeDistance / cascadeLevelScale; + } + + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearDepth; - } else { - minCascadeDistance -= shadowOverlapDistance; } minCascadeDistance = std::max(minCascadeDistance, nearDepth); maxCascadeDistance = std::min(maxCascadeDistance, farClip); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 6408ad472a..e548af6a79 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -43,10 +43,9 @@ float getShadowBias(int cascadeIndex) { // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { - float bias = -getShadowBias(cascadeIndex); - vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); + float bias = getShadowBias(cascadeIndex); + return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); } int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { From 2a192475304a1894af9164b4f1c9a27749c8afce Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 19:09:35 +0100 Subject: [PATCH 039/137] Switched back to 3 cascades --- libraries/render-utils/src/LightStage.cpp | 4 +++- libraries/render-utils/src/LightStage.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 6808c75914..7c5ea817b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -21,7 +21,9 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0 }; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; +const int LightStage::Shadow::MAP_SIZE = 1024; + +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 9f395c905a..41a6ce945e 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -46,7 +46,7 @@ public: class Shadow { public: using UniformBufferView = gpu::BufferView; - static const int MAP_SIZE = 1024; + static const int MAP_SIZE; class Cascade { friend Shadow; From 41e929c8a2b330e640c1a77745e68b58988cf839 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 10:58:03 +1300 Subject: [PATCH 040/137] Add missing types to typedefs --- libraries/script-engine/src/MenuItemProperties.cpp | 2 +- libraries/script-engine/src/Quat.h | 2 +- libraries/script-engine/src/Vec3.h | 6 +++--- libraries/shared/src/RegisteredMetaTypes.h | 2 +- libraries/shared/src/shared/Camera.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index 798fe27b79..40254eeccb 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -56,7 +56,7 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * If none of position, beforeItem, afterItem, or grouping are specified, * the menu item will be placed at the end of the menu. * - * @typedef {Object} Menu.MenuItemProperties + * @typedef {object} Menu.MenuItemProperties * @property {string} menuName Name of the menu. Nested menus can be described using the ">" symbol. * @property {string} menuItemName Name of the menu item. * @property {boolean} [isCheckable=false] Whether or not the menu item is checkable. diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index d3fb2ed5c2..879238b0a2 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -23,7 +23,7 @@ /**jsdoc * A Quaternion * - * @typedef Quat + * @typedef {object} Quat * @property {float} x imaginary component i. * @property {float} y imaginary component j. * @property {float} z imaginary component k. diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index c7179a80c0..1e1cd917a3 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -24,7 +24,7 @@ /**jsdoc * A 2-dimensional vector. * - * @typedef Vec2 + * @typedef {object} Vec2 * @property {float} x X-coordinate of the vector. * @property {float} y Y-coordinate of the vector. */ @@ -32,7 +32,7 @@ /**jsdoc * A 3-dimensional vector. * - * @typedef Vec3 + * @typedef {object} Vec3 * @property {float} x X-coordinate of the vector. * @property {float} y Y-coordinate of the vector. * @property {float} z Z-coordinate of the vector. @@ -41,7 +41,7 @@ /**jsdoc * A 4-dimensional vector. * - * @typedef Vec4 + * @typedef {object} Vec4 * @property {float} x X-coordinate of the vector. * @property {float} y Y-coordinate of the vector. * @property {float} z Z-coordinate of the vector. diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index eb790ae314..8644cc7925 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -129,7 +129,7 @@ void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube); * A PickRay defines a vector with a starting point. It is used, for example, when finding entities or overlays that lie under a * mouse click or intersect a laser beam. * - * @typedef PickRay + * @typedef {object} PickRay * @property {Vec3} origin - The starting position of the PickRay. * @property {Quat} direction - The direction that the PickRay travels. */ diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index f262febbc7..ced50f7760 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -135,7 +135,7 @@ void Camera::loadViewFrustum(ViewFrustum& frustum) const { * A ViewFrustum has a "keyhole" shape: a regular frustum for stuff that is visible plus a central sphere for stuff that is * nearby (for physics simulation). * - * @typedef ViewFrustum + * @typedef {object} ViewFrustum * @property {Vec3} position - The location of the frustum's apex. * @property {Quat} orientation - The direction that the frustum is looking at. * @property {number} centerRadius - Center radius of the keyhole in meters. From a29a3ff88dc5d95e4d4da995facc2907105db0b4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 12:58:03 +1300 Subject: [PATCH 041/137] Paths API JSDoc improvements --- libraries/shared/src/PathUtils.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index a59e0f39ab..320865d667 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -19,10 +19,13 @@ #include "DependencyManager.h" /**jsdoc + * The Paths API provides absolute paths to the scripts and resources directories. + * * @namespace Paths + * @deprecated The Paths API is deprecated. Use {@link Script.resolvePath} and {@link Script.resourcesPath} instead. * @readonly + * @property {string} defaultScripts The path to the scripts directory. Readonly * @property {string} resources The path to the resources directory. Readonly - * @property {string} defaultScripts The path to the defaultScripts directory. Readonly */ class PathUtils : public QObject, public Dependency { Q_OBJECT From a35316913e7167b6bc2bdec302dd8947f905b516 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 12:58:33 +1300 Subject: [PATCH 042/137] Uuid API JSDoc fixes --- libraries/script-engine/src/ScriptUUID.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index bf828f5646..303a871d1d 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -18,7 +18,7 @@ #include /**jsdoc - * A UUID (Universally Unique IDentitier) is used to uniquely identify entities, overlays, avatars, and the like. It is + * A UUID (Universally Unique IDentifier) is used to uniquely identify entities, overlays, avatars, and the like. It is * represented in JavaScript as a string in the format, {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}, where the "n"s are * hexadecimal digits. * @@ -32,7 +32,6 @@ class ScriptUUID : public QObject, protected QScriptable { Q_PROPERTY(QString NULL READ NULL_UUID CONSTANT) // String for use in scripts. public slots: - QUuid fromString(const QString& string); /**jsdoc * Generates a UUID from a string representation of the UUID. * @function Uuid.fromString @@ -48,6 +47,7 @@ public slots: * uuid = Uuid.fromString("527c27ea"); * print(uuid); // null */ + QUuid fromString(const QString& string); /**jsdoc * Generates a string representation of a UUID. However, because UUIDs are represented in JavaScript as strings, this is in From bdb1bbe88df123aeb9d2c1a9882764554237e2c9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 13:34:50 +1300 Subject: [PATCH 043/137] Menu API JSDoc improvements --- .../src/scripting/MenuScriptingInterface.h | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index b5a11581cb..48d480c4dd 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -32,22 +32,6 @@ class MenuItemProperties; * If a menu item doesn't belong to a group it is always displayed. * * @namespace Menu - * - * @example Add a menu and menu item, and detect clicks on the menu item added. - * Menu.addMenu("Demo Menu"); - * - * Menu.addMenuItem({ - * menuName: "Demo Menu", - * menuItemName: "Demo Item" - * }); - * - * function onMenuItemEvent(menuItem) { - * if(menuItem === "Demo Item") { - * print("Nice! You clicked the demo menu item."); - * } - * } - * - * Menu.menuItemEvent.connect(menuItemEventHandler); */ /** @@ -74,18 +58,22 @@ public slots: /**jsdoc * Add a new top-level menu. * @function Menu.addMenu - * @param {string} menuName Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. - * @param {string} [grouping] Name of the grouping, if any, to add this menu to. + * @param {string} menuName - Name that will be displayed for the menu. Nested menus can be described using the ">" symbol. + * @param {string} [grouping] - Name of the grouping, if any, to add this menu to. + * * @example Add a menu and a nested submenu. * Menu.addMenu("Test Menu"); * Menu.addMenu("Test Menu > Test Sub Menu"); + * + * @example Add a menu to the Settings menu that is only visible if Settings > Advanced is enabled. + * Menu.addMenu("Settings > Test Grouping Menu", "Advanced"); */ void addMenu(const QString& menuName, const QString& grouping = QString()); /**jsdoc * Remove a top-level menu. * @function Menu.removeMenu - * @param {string} menuName Name of the menu to remove. + * @param {string} menuName - Name of the menu to remove. * @example Remove a menu and nested submenu. * Menu.removeMenu("Test Menu > Test Sub Menu"); * Menu.removeMenu("Test Menu"); @@ -109,8 +97,8 @@ public slots: * If you want to add a separator at a specific point in the menu, use {@link Menu.addMenuItem} with * {@link Menu.MenuItemProperties} instead. * @function Menu.addSeparator - * @param {string} menuName Name of the menu to add a separator to. - * @param {string} separatorName Name of the separator that will be displayed as the label below the separator line. + * @param {string} menuName - Name of the menu to add a separator to. + * @param {string} separatorName - Name of the separator that will be displayed as the label below the separator line. * @example Add a separator. * Menu.addSeparator("Developer","Test Separator"); */ @@ -119,8 +107,8 @@ public slots: /**jsdoc * Remove a separator from a menu. * @function Menu.removeSeparator - * @param {string} menuName Name of the menu to remove the separator from. - * @param {string} separatorName Name of the separator to remove. + * @param {string} menuName - Name of the menu to remove the separator from. + * @param {string} separatorName - Name of the separator to remove. * @example Remove a separator. * Menu.removeSeparator("Developer","Test Separator"); */ @@ -129,7 +117,7 @@ public slots: /**jsdoc * Add a new menu item to a menu. * @function Menu.addMenuItem - * @param {Menu.MenuItemProperties} properties Properties of the menu item to create. + * @param {Menu.MenuItemProperties} properties - Properties of the menu item to create. * @example Add a menu item using {@link Menu.MenuItemProperties}. * Menu.addMenuItem({ * menuName: "Developer", @@ -144,8 +132,8 @@ public slots: /**jsdoc * Add a new menu item to a menu. The new item is added at the end of the menu. * @function Menu.addMenuItem - * @param {string} menuName Name of the menu to add a menu item to. - * @param {string} menuItem Name of the menu item. This is what will be displayed in the menu. + * @param {string} menuName - Name of the menu to add a menu item to. + * @param {string} menuItem - Name of the menu item. This is what will be displayed in the menu. * @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item. * @example Add a menu item to the end of the "Developer" menu. * Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T"); @@ -156,8 +144,8 @@ public slots: /**jsdoc * Remove a menu item from a menu. * @function Menu.removeMenuItem - * @param {string} menuName Name of the menu to remove a menu item from. - * @param {string} menuItem Name of the menu item to remove. + * @param {string} menuName - Name of the menu to remove a menu item from. + * @param {string} menuItem - Name of the menu item to remove. * Menu.removeMenuItem("Developer", "Test"); */ void removeMenuItem(const QString& menuName, const QString& menuitem); @@ -165,8 +153,8 @@ public slots: /**jsdoc * Check if a menu item exists. * @function Menu.menuItemExists - * @param {string} menuName Name of the menu that the menu item is in. - * @param {string} menuItem Name of the menu item to check for existence of. + * @param {string} menuName - Name of the menu that the menu item is in. + * @param {string} menuItem - Name of the menu item to check for existence of. * @returns {boolean} true if the menu item exists, otherwise false. * @example Determine if the Developer > Stats menu exists. * if (Menu.menuItemExists("Developer", "Stats")) { @@ -185,40 +173,52 @@ public slots: /**jsdoc * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked - * @param {string} menuOption The name of the menu item. + * @param {string} menuOption - The name of the menu item. * @returns {boolean} true if the option is checked, otherwise false. + * @example Report whether the Settings > Advanced menu item is turned on. + * print(Menu.isOptionChecked("Advanced Menus")); // true or false */ bool isOptionChecked(const QString& menuOption); /**jsdoc * Set a checkable menu item as checked or unchecked. * @function Menu.setIsOptionChecked - * @param {string} menuOption The name of the menu item to modify. - * @param {boolean} isChecked If true, the menu item will be checked, otherwise it will not be checked. + * @param {string} menuOption - The name of the menu item to modify. + * @param {boolean} isChecked - If true, the menu item will be checked, otherwise it will not be checked. + * @example Turn on Settings > Advanced Menus. + * Menu.setIsOptionChecked("Advanced Menus", true); + * print(Menu.isOptionChecked("Advanced Menus")); // true */ void setIsOptionChecked(const QString& menuOption, bool isChecked); /**jsdoc * Trigger the menu item as if the user clicked on it. * @function Menu.triggerOption - * @param {string} menuOption The name of the menu item to trigger. + * @param {string} menuOption - The name of the menu item to trigger. + * @example Open the help window. + * Menu.triggerOption('Help...'); */ void triggerOption(const QString& menuOption); /**jsdoc - * Check whether a menu is enabled. If a menu is disabled it is grayed out and unusable. + * Check whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable. * Menus are enabled by default. * @function Menu.isMenuEnabled * @param {string} menuName The name of the menu to check. * @returns {boolean} true if the menu is enabled, otherwise false. + * @example Report with the Settings > Advanced Menus menu item is enabled. + * print(Menu.isMenuEnabled("Settings > Advanced Menus")); // true or false */ bool isMenuEnabled(const QString& menuName); /**jsdoc - * Set a menu to be enabled or disabled. If a menu is disabled it is grayed out and unusable. + * Set a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable. * @function Menu.setMenuEnabled - * @param {string} menuName The name of the menu to modify. - * @param {boolean} isEnabled If true, the menu will be enabled, otherwise it will be disabled. + * @param {string} menuName - The name of the menu to modify. + * @param {boolean} isEnabled - If true, the menu will be enabled, otherwise it will be disabled. + * @example Disable the Settings > Advanced Menus menu item. + * Menu.setMenuEnabled("Settings > Advanced Menus", false); + * print(Menu.isMenuEnabled("Settings > Advanced Menus")); // false */ void setMenuEnabled(const QString& menuName, bool isEnabled); @@ -232,7 +232,7 @@ signals: /**jsdoc * Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}). * @function Menu.menuItemEvent - * @param {string} menuItem Name of the menu item that was clicked. + * @param {string} menuItem - Name of the menu item that was clicked. * @returns {Signal} * @example Detect menu item events. * function onMenuItemEvent(menuItem) { From 4f083a1b2029bed95a5698ec06618fe5d4d9fb40 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 13:39:14 +1300 Subject: [PATCH 044/137] Further Menu API JSDoc improvements --- interface/src/scripting/MenuScriptingInterface.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 48d480c4dd..59cfc76d21 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -204,7 +204,7 @@ public slots: * Check whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable. * Menus are enabled by default. * @function Menu.isMenuEnabled - * @param {string} menuName The name of the menu to check. + * @param {string} menuName The name of the menu or menu item to check. * @returns {boolean} true if the menu is enabled, otherwise false. * @example Report with the Settings > Advanced Menus menu item is enabled. * print(Menu.isMenuEnabled("Settings > Advanced Menus")); // true or false @@ -214,7 +214,7 @@ public slots: /**jsdoc * Set a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable. * @function Menu.setMenuEnabled - * @param {string} menuName - The name of the menu to modify. + * @param {string} menuName - The name of the menu or menu item to modify. * @param {boolean} isEnabled - If true, the menu will be enabled, otherwise it will be disabled. * @example Disable the Settings > Advanced Menus menu item. * Menu.setMenuEnabled("Settings > Advanced Menus", false); From cc43d8115c3fff02e5aa0d878f0eeabf427bb248 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 15 Nov 2017 17:41:33 -0800 Subject: [PATCH 045/137] In response to bug in the original PR, in RenderModelEntityItem.cpp I made a change in the handling of the boundary cases for the first and last frame properties. Also deleted some extraneous comments. --- .../src/RenderableModelEntityItem.cpp | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3ed0bdfc43..aedc649470 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -980,8 +980,6 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - //added by angus - //this a spot to work on the avatar npc in my branch QVector jointsData; @@ -1000,7 +998,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { if ((firstFrame >= 0) && (firstFrame < lastFrame) && (lastFrame <= frameCount)) { //length of animation in now determined by first and last frame - updatedFrameCount = lastFrame - firstFrame; + updatedFrameCount = (lastFrame - firstFrame + 1); } @@ -1016,14 +1014,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { //here we implement the looping animation property //if we have played through the animation once then we hold on the last frame - // if (!isLooping && (_currentFrame > _endAnim)) { - //don't advance current frame!!! - - //}else{ + if( isLooping || ( _currentFrame < _endAnim ) ){ //else advance the current frame. //if hold or not playing don't advance the current frame. - if (!isHolding && entity->getAnimationIsPlaying()) { + //also if the animFrame is outside of first or last frame then don't advance the motion. + if (!isHolding && entity->getAnimationIsPlaying() && !( _renderAnimationProperties.getCurrentFrame() > _renderAnimationProperties.getLastFrame() ) && !( _renderAnimationProperties.getCurrentFrame() < _renderAnimationProperties.getFirstFrame() ) ) { float deltaTime = (float)interval / (float)USECS_PER_SECOND; _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); } @@ -1362,17 +1358,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (newAnimationProperties != _renderAnimationProperties) { withWriteLock([&] { if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { - _currentFrame = newAnimationProperties.getCurrentFrame(); - _endAnim = (_currentFrame - 1) + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())); - _lastAnimated = 0; - //qCDebug(entitiesrenderer) << "reset current frame" << _endAnim; - } - if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { - //set the end of animation relative to the current frame - //qCDebug(entitiesrenderer) << "turned off looping"; - float startOffset = newAnimationProperties.getCurrentFrame() - newAnimationProperties.getFirstFrame(); - float posRelativeToStart = (_currentFrame - newAnimationProperties.getFirstFrame()) - startOffset; - _endAnim = (_currentFrame - 1) + (((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - ((int)(glm::floor(posRelativeToStart)) % ((int)newAnimationProperties.getLastFrame() - (int)newAnimationProperties.getFirstFrame()))); + if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { + _currentFrame = newAnimationProperties.getCurrentFrame(); + _endAnim = _currentFrame + ( newAnimationProperties.getLastFrame() - newAnimationProperties.getFirstFrame() ); + _lastAnimated = 0; + } + }else if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { + int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); + _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; } _renderAnimationProperties = newAnimationProperties; }); From d3c33b1aaf93d4830d12218e837ff581024bc3e3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Nov 2017 16:20:54 +1300 Subject: [PATCH 046/137] Quat API JSDoc --- libraries/script-engine/src/Quat.h | 321 +++++++++++++++++++++++++++-- 1 file changed, 308 insertions(+), 13 deletions(-) diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 879238b0a2..ff66ef9d61 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -21,13 +21,22 @@ #include /**jsdoc - * A Quaternion - * + * A quaternion value. See also the [Quat]{@link Quat(0)} object. * @typedef {object} Quat - * @property {float} x imaginary component i. - * @property {float} y imaginary component j. - * @property {float} z imaginary component k. - * @property {float} w real component. + * @property {number} x imaginary component i. + * @property {number} y imaginary component j. + * @property {number} z imaginary component k. + * @property {number} w real component. + */ + +/**jsdoc + * The Quat API provides facilities for generating and manipulating quaternions. + * @namespace Quat + * @variation 0 + * @property IDENTITY {Quat} The identity rotation, i.e., no rotation. + * @example Print the IDENTITY value. + * print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 } + * print(JSON.stringify(Quat.safeEulerAngles(Quat.IDENTITY))); // { x: 0, y: 0, z: 0 } */ /// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API @@ -36,33 +45,319 @@ class Quat : public QObject, protected QScriptable { Q_PROPERTY(glm::quat IDENTITY READ IDENTITY CONSTANT) public slots: + + /**jsdoc + * Multiply two quaternions. + * @function Quat(0).multiply + * @param {Quat} q1 - The first quaternion. + * @param {Quat} q2 - The second quaternion. + * @returns {Quat} q1 multiplied with q2. + */ glm::quat multiply(const glm::quat& q1, const glm::quat& q2); + + /**jsdoc + * Normalizes a quaternion. + * @function Quat(0).normalize + * @param {Quat} q - The quaternion to normalize. + * @returns {Quat} q normalized to have unit length. + */ glm::quat normalize(const glm::quat& q); + + /**jsdoc + * Calculate the conjugate of a quaternion. Synonym for [Quat.inverse]{@link Quat(0).inverse}. + * @function Quat(0).conjugate + * @param {Quat} q - The quaternion to conjugate. + * @returns {Quat} The conjugate (inverse) of q. + * @example A quaternion multiplied by its conjugate is a zero rotation. + * var quaternion = Quat.fromPitchYawRollDegrees(10, 20, 30); + * Quat.print("quaternion", quaternion, true); // dvec3(10.000000, 20.000004, 30.000004) + * var conjugate = Quat.conjugate(quaternion); + * Quat.print("conjugate", conjugate, true); // dvec3(1.116056, -22.242186, -28.451778) + * var identity = Quat.multiply(conjugate, quaternion); + * Quat.print("identity", identity, true); // dvec3(0.000000, 0.000000, 0.000000) + */ glm::quat conjugate(const glm::quat& q); + + /**jsdoc + * Calculate a camera orientation given eye position, point of interest, and "up" direction. The camera's negative z-axis is + * the forward direction. The result has zero roll about its forward direction with respect to the given "up" direction. + * @function Quat(0).lookAt + * @param {Vec3} eye - The eye position. + * @param {Vec3} center - A point that the eye's looking at; the center of the scene. + * @param {Vec3} up - The "up" direction. + * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-center vector and the x-axis to + * be the cross product of the eye-to-center and up vectors. + */ glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); + + /**jsdoc + * Calculate a camera orientation given eye position and point of interest. The camera's negative z-axis is the forward + * direction. The result has zero roll about its forward direction. + * @function Quat(0).lookAtSimple + * @param {Vec3} eye - The eye position. + * @param {Vec3} center - A point that the eye's looking at; the center of the scene. + * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-center vector and the x-axis to be the + * cross product of the eye-to-center and an "up" vector. The "up" vector is the y-axis unless the eye-to-center vector + * is nearly aligned with it, in which case the x-axis is used as the "up" vector. + * @example Demonstrate that the calculation orients the negative z-axis. + * var eye = Vec3.ZERO; + * var center = { x: 0, y: 0, z: 1 } + * var orientation = Quat.lookAtSimple(eye, center); + * var eulers = Quat.safeEulerAngles(orientation); + * print(JSON.stringify(eulers)); // {"x":0,"y":-180,"z":0} + */ glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); + + /**jsdoc + * Calculate the shortest rotation from a first vector onto a second. + * @function Quat(0).rotationBetween + * @param {Vec3} v1 - The first vector. + * @param {Vec3} v2 - The second vector. + * @returns {Quat} The rotation from v1 onto v2. + */ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); - glm::quat fromVec3Degrees(const glm::vec3& vec3); // degrees - glm::quat fromVec3Radians(const glm::vec3& vec3); // radians - glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees - glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); // radians + + /**jsdoc + * Generate a quaternion from a {@link Vec3} of Euler angles in degrees. + * @function Quat(0).fromVec3Degrees + * @param {Vec3} vector - A vector of three Euler angles in degrees. + * @returns {Quat} A quaternion created from the Euler angles in vector. + */ + glm::quat fromVec3Degrees(const glm::vec3& vec3); + + /**jsdoc + * Generate a quaternion from a {@link Vec3} of Euler angles in radians. + * @function Quat(0).fromVec3Radians + * @param {Vec3} vector - A vector of three Euler angles in radians. + * @returns {Quat} A quaternion created using the Euler angles in vector. + */ + glm::quat fromVec3Radians(const glm::vec3& vec3); + + /**jsdoc + * Generate a quaternion from pitch, yaw, and roll values in degrees. + * @function Quat(0).fromPitchYawRollDegrees + * @param {number} pitch - The pitch angle in degrees. + * @param {number} yaw - The yaw angle in degrees. + * @param {number} roll - The roll angle in degrees. + * @returns {Quat} A quaternion created using the pitch, yaw, and roll angles. + */ + glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); + + /**jsdoc + * Generate a quaternion from pitch, yaw, and roll values in radians. + * @function Quat(0).fromPitchYawRollRadians + * @param {number} pitch - The pitch angle in radians. + * @param {number} yaw - The yaw angle in radians. + * @param {number} roll - The roll angle in radians. + * @returns {Quat} A quaternion created from the pitch, yaw, and roll angles. + */ + glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); + + /**jsdoc + * Calculate the inverse of a quaternion. Synonym for [Quat.conjugate]{@link Quat(0).conjugate}. + * @function Quat(0).inverse + * @param {Quat} q - The quaternion. + * @returns {Quat} The inverse (conjugate) of q. + */ glm::quat inverse(const glm::quat& q); - // redundant, calls getForward which better describes the returned vector as a direction + + /**jsdoc + * Get the negative z-axis for the quaternion. This is a synonym for [Quat.getForward]{@link Quat(0).getForward}. + * @function Quat(0).getFront + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Vec3} The negative z-axis rotated by orientation. + */ glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } + + /**jsdoc + * Get the negative z-axis for the quaternion. This is a synonym for [Quat.getFront]{@link Quat(0).getFront}. + * @function Quat(0).getForward + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Vec3} The negative z-axis rotated by orientation. + * @example Demonstrate that the "forward" vector is for the negative z-axis. + * var forward = Quat.getForward(Quat.IDENTITY); + * print(JSON.stringify(forward)); // {"x":0,"y":0,"z":-1} + */ glm::vec3 getForward(const glm::quat& orientation); + + /**jsdoc + * Get the x-axis for the quaternion. + * @function Quat(0).getRight + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Vec3} The x-axis rotated by orientation. + */ glm::vec3 getRight(const glm::quat& orientation); + + /**jsdoc + * Get the y-axis for the quaternion. + * @function Quat(0).getUp + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Vec3} The y-axis rotated by orientation. + */ glm::vec3 getUp(const glm::quat& orientation); - glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees - glm::quat angleAxis(float angle, const glm::vec3& v); // degrees + + /**jsdoc + * Calculate the Euler angles for the quaternion, in degrees. (The "safe" in the name signifies that the angle results will + * not be garbage even when the rotation is particularly difficult to decompose.) + * @function Quat(0).safeEulerAngles + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Vec3} A {@link Vec3} of Euler angles for the orientation, in degrees. + */ + glm::vec3 safeEulerAngles(const glm::quat& orientation); + + /**jsdoc + * Generate a quaternion given an angle to rotate through and an axis to rotate about. + * @function Quat(0).angleAxis + * @param {number} angle - The angle to rotate through, in degrees. + * @param {Vec3} axis - The axis to rotate about. + * @returns {Quat} A quaternion that is a rotation through angle degrees about the axis. + * WARNING: This value is in degrees whereas the value returned by [Quat.angle]{@link Quat(0).angleAxis} is + * in radians. + */ + glm::quat angleAxis(float angle, const glm::vec3& v); + + /**jsdoc + * Get the rotation axis for a quaternion. + * @function Quat(0).axis + * @param {Quat} q - The quaternion. + * @returns {Vec3} The normalized rotation axis for q. + */ glm::vec3 axis(const glm::quat& orientation); + + /**jsdoc + * Get the rotation angle for a quaternion. + * @function Quat(0).angle + * @param {Quat} q - The quaternion. + * @returns {Vec3} The rotation angle for q, in radians. WARNING: This value is in radians + * whereas the value used by [Quat.angleAxis]{@link Quat(0).angleAxis} is in degrees. + */ float angle(const glm::quat& orientation); + + // spherical linear interpolation + // alpha: 0.0 to 1.0? + /**jsdoc + * Compute a spherical linear interpolation between two rotations, safely handling two rotations that are very similar. + * See also, [Quat.slerp]{@link Quat(0).slerp}. + * @function Quat(0).mix + * @param {Quat} q1 - The beginning rotation. + * @param {Quat} q2 - The ending rotation. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + * @example Animate between one rotation and another. + * var dt = amountOfTimeThatHasPassed; + * var mixFactor = amountOfTimeThatHasPassed / TIME_TO_COMPLETE; + * if (mixFactor) > 1) { + * mixFactor = 1; + * } + * var newRotation = Quat.mix(startRotation, endRotation, mixFactor); + */ glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); + + /**jsdoc + * Compute a spherical linear interpolation between two rotations. + * See also, [Quat.mix]{@link Quat(0).mix}. + * @function Quat(0).slerp + * @param {Quat} q1 - The beginning rotation. + * @param {Quat} q2 - The ending rotation. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + */ glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); + + /**jsdoc + * Compute a spherical quadratic interpolation between two rotations. + * @function Quat(0).squad + * @param {Quat} q1 - The rotation before to the beginning rotation. + * @param {Quat} q2 - The beginning rotation. + * @param {Quat} q3 - The ending rotation. + * @param {Quat} q4 - The rotation after the ending rotation. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + */ glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); + + /**jsdoc + * Calculate the dot product of two quaternions. + * A zero value means the rotations are completely orthogonal to each other. The closer the rotations are to each other the + * more non-zero the value is (either positive or negative). Identical rotations have a dot product of +/- 1. + * @function Quat(0).dot + * @param {Quat} q1 - The first quaternion. + * @param {Quat} q2 - The second quaternion. + * @returns {Quat} The dot product of q1 and q2. + */ float dot(const glm::quat& q1, const glm::quat& q2); + + /**jsdoc + * Print to the program log a text label followed by a quaternion's pitch, yaw, and roll Euler angles. + * @function Quat(0).print + * @param {string} label - The label to print. + * @param {Quat} q - The quaternion to print. + * @param {boolean} [asDegrees=false] - Whether to print the angles in degrees. + * @example Two ways of printing a label plus a quaternion's Euler angles. + * var quaternion = Quat.fromPitchYawRollDegrees(0, 45, 0); + * + * // Quaternion: dvec3(0.000000, 45.000004, 0.000000) + * Quat.print("Quaternion:", quaternion, true); + * + * // Quaternion: {"x":0,"y":45.000003814697266,"z":0} + * print("Quaternion: " + JSON.stringify(Quat.safeEulerAngles(quaternion))); + */ void print(const QString& label, const glm::quat& q, bool asDegrees = false); + + /**jsdoc + * Test whether two quaternions are equal. Note: The quaternions must be exactly equal in order for + * true to be returned; it is often better to use [Quat.dot]{@link Quat(0).dot} and test for closeness to +/-1. + * @function Quat(0).equal + * @param {Quat} q1 - The first quaternion. + * @param {Quat} q2 - The second quaternion. + * @returns {boolean} true if the quaternions are equal, otherwise false. + * @example Testing quaternions for equality. + * var q1 = Quat.fromPitchYawRollDegrees(0, 0, 0); + * var q2 = Quat.fromPitchYawRollDegrees(0, 0, 0); + * print(Quat.equal(q1, q2)); // true + * var q3 = Quat.fromPitchYawRollDegrees(0, 0, 359.95); + * print(Quat.equal(q1, q3)); // false + * + * var dot = Quat.dot(q1, q3); + * print(dot); // -0.9999999403953552 + * var equal = Math.abs(1 - Math.abs(dot)) < 0.000001; + * print(equal); // true + */ bool equal(const glm::quat& q1, const glm::quat& q2); + + /**jsdoc + * Cancels out the roll and pitch component of a quaternion so that its completely horizontal with a yaw pointing in the + * given quaternion's direction. + * @function Quat(0).cancelOutRollAndPitch + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Quat} orientation with its roll and pitch canceled out. + * @example Two ways of calculating a camera orientation in the x-z plane with a yaw pointing in the direction of + * a given quaternion. + * var quaternion = Quat.fromPitchYawRollDegrees(10, 20, 30); + * + * var noRollOrPitch = Quat.cancelOutRollAndPitch(quaternion); + * Quat.print("", noRollOrPitch, true); // dvec3(0.000000, 22.245995, 0.000000) + * + * var front = Quat.getFront(quaternion); + * var lookAt = Quat.lookAtSimple(Vec3.ZERO, { x: front.x, y: 0, z: front.z }); + * Quat.print("", lookAt, true); // dvec3(0.000000, 22.245996, 0.000000) + * + */ glm::quat cancelOutRollAndPitch(const glm::quat& q); + + /**jsdoc + * Cancels out the roll component of a quaternion so that its horizontal axis is level. + * @function Quat(0).cancelOutRoll + * @param {Quat} orientation - A quaternion representing an orientation. + * @returns {Quat} orientation with its roll canceled out. + * @example Two ways of calculating a camera orientation that points in the direction of a given quaternion but + * keeps the camera's horizontal axis level. + * var quaternion = Quat.fromPitchYawRollDegrees(10, 20, 30); + * + * var noRoll = Quat.cancelOutRoll(quaternion); + * Quat.print("", noRoll, true); // dvec3(-1.033004, 22.245996, -0.000000) + * + * var front = Quat.getFront(quaternion); + * var lookAt = Quat.lookAtSimple(Vec3.ZERO, front); + * Quat.print("", lookAt, true); // dvec3(-1.033004, 22.245996, -0.000000) + */ glm::quat cancelOutRoll(const glm::quat& q); private: From e574be785573c84a60ff486e969403cac522f031 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 10:00:32 +0100 Subject: [PATCH 047/137] Fixed potential mismatch between push/popViewFrustum in shadow task --- libraries/render-utils/src/RenderShadowTask.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7a6e3dc74f..2dd6c5d44f 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -263,7 +263,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; - // Reset the render args - args->popViewFrustum(); + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { + // Reset the render args + args->popViewFrustum(); + } + assert(args->hasViewFrustum()); args->_renderMode = input; }; From d2c28c3c2163be31fc5660ee9aba47627a0e28a8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 10:49:59 +0100 Subject: [PATCH 048/137] Fixed disappearing shadow bug each time a domain was automatically reloaded --- libraries/render-utils/src/LightStage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 188cb46157..f0aa029646 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -147,7 +147,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { _descs.emplace_back(Desc()); } else { assert(_descs[lightId].shadowId == INVALID_INDEX); - _descs.emplace(_descs.begin() + lightId, Desc()); + _descs[lightId] = Desc(); } // INsert the light and its index in the reverese map From 917ce7165c6e028009eea93c3a8f73f4ffad3f35 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 15:36:48 +0100 Subject: [PATCH 049/137] Working shadow cascade debugging view in Luci app --- .../render-utils/src/DebugDeferredBuffer.cpp | 4 +-- .../render-utils/src/RenderDeferredTask.cpp | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index eda6c69f88..3902f4d9d4 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -166,13 +166,13 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ " ivec2 cascadeIndices;" " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" - " }" + "}" }; static const std::string DEFAULT_LINEAR_DEPTH_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);" - " }" + "}" }; static const std::string DEFAULT_HALF_LINEAR_DEPTH_SHADER{ diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 978af72ad5..d2606bf42f 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -211,22 +211,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawSelectionBounds", selectedItems); } - // Layered Overlays - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); - const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); - const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); - - const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); - const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); - task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); - task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); - - { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer - task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); - task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); - } - // Debugging stages { // Debugging Deferred buffer job @@ -257,6 +241,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawZoneStack", deferredFrameTransform); } + // Layered Overlays + const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); + const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); + const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); + const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); + + const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); + const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); + task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); + task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); + + { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer + task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); + task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); + } + // AA job to be revisited task.addJob("Antialiasing", primaryFramebuffer); From 881b22ab159d637561971b63596d7e176cf5552e Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 16 Nov 2017 12:41:56 -0800 Subject: [PATCH 050/137] added update function to modelEntityItem.cpp --- .../src/RenderableModelEntityItem.cpp | 1 + libraries/entities/src/ModelEntityItem.cpp | 23 +++++++++++++++++++ libraries/entities/src/ModelEntityItem.h | 2 ++ 3 files changed, 26 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index aedc649470..e78e609699 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1356,6 +1356,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // make a copy of the animation properites auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { + qCDebug(entitiesrenderer) << "this is where the change is currently handled in the rendering code"; withWriteLock([&] { if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index ae73fa684a..515d76a9b3 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -186,6 +186,29 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit } +//angus +/* +void ModelEntityItem::update(const quint64& now) { + + //put something here + qCDebug(entities) << "model entity item update"; + + +} + +bool ModelEntityItem::needsToCallUpdate() const { + + + //put something here + qCDebug(entities) << "needs to call update"; + return true; +} +*/ +//angus + + + + void ModelEntityItem::debugDump() const { qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID(); qCDebug(entities) << " edited ago:" << getEditedAgo(); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 698197b1a6..4f13fbdf6a 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -46,8 +46,10 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + //angus //virtual void update(const quint64& now) override; //virtual bool needsToCallUpdate() const override; + //angus virtual void debugDump() const override; void setShapeType(ShapeType type) override; From 20fd893b47dfd3b2214c830fd56646c81393e8a6 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 16 Nov 2017 18:38:28 -0800 Subject: [PATCH 051/137] Starting to implement the update function to ModelEntityItem_cpp Also put access to the currently playing frame in RenderableModelEntityItem_cpp --- .../src/RenderableModelEntityItem.cpp | 4 +- .../entities/src/AnimationPropertyGroup.cpp | 12 ++++++ .../entities/src/AnimationPropertyGroup.h | 1 + libraries/entities/src/ModelEntityItem.cpp | 42 +++++++++++++++---- libraries/entities/src/ModelEntityItem.h | 10 ++++- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e78e609699..2ba8a7a45f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -971,9 +971,6 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit entity->setModel({}); } -bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return !(a == b); -} void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { if (!_animation || !_animation->isLoaded()) { @@ -1357,6 +1354,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { qCDebug(entitiesrenderer) << "this is where the change is currently handled in the rendering code"; + qCDebug(entitiesrenderer) << "getting the currently playing frame from the modelentityitem update" << entity->getCurrentlyPlayingFrame(); withWriteLock([&] { if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index d6a2937553..c871849064 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -31,6 +31,18 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b (a._hold == b._hold); } +bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { + return + (a._url != b._url) || + (a._currentFrame != b._currentFrame) || + (a._running != b._running) || + (a._loop != b._loop) || + (a._firstFrame != b._firstFrame) || + (a._lastFrame != b._lastFrame) || + (a._hold != b._hold); +} + + void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index affa960d66..54d4ced92f 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -89,6 +89,7 @@ public: protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); + friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); void setFromOldAnimationSettings(const QString& value); }; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 515d76a9b3..c24efa8eec 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -187,12 +187,33 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit //angus -/* + void ModelEntityItem::update(const quint64& now) { //put something here - qCDebug(entities) << "model entity item update"; + //qCDebug(entities) << "model entity item update" << getName() << " " << getEntityItemID(); + { + auto currentAnimationProperties = this->getAnimationProperties(); + + if (_previousAnimationProperties != currentAnimationProperties) { + qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code"; + withWriteLock([&] { + //if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { + // if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { + // _currentFrame = newAnimationProperties.getCurrentFrame(); + // _endAnim = _currentFrame + ( newAnimationProperties.getLastFrame() - newAnimationProperties.getFirstFrame() ); + //_lastAnimated = 0; + // } + //}else if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { + // int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); + // _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + // } + _previousAnimationProperties = currentAnimationProperties; + }); + } + + } } @@ -200,10 +221,10 @@ bool ModelEntityItem::needsToCallUpdate() const { //put something here - qCDebug(entities) << "needs to call update"; + //qCDebug(entities) << "needs to call update"; return true; } -*/ + //angus @@ -603,8 +624,9 @@ float ModelEntityItem::getAnimationLastFrame() const { return _animationProperties.getLastFrame(); }); } +//angus change bool ModelEntityItem::getAnimationIsPlaying() const { - return resultWithReadLock([&] { + return resultWithReadLock([&] { return _animationProperties.getRunning(); }); } @@ -614,11 +636,17 @@ float ModelEntityItem::getAnimationCurrentFrame() const { return _animationProperties.getCurrentFrame(); }); } - +//angus change bool ModelEntityItem::isAnimatingSomething() const { - return resultWithReadLock([&] { + return resultWithReadLock([&] { return !_animationProperties.getURL().isEmpty() && _animationProperties.getRunning() && (_animationProperties.getFPS() != 0.0f); }); } + +float ModelEntityItem::getCurrentlyPlayingFrame() const { + return resultWithReadLock([&] { + return _currentlyPlayingFrame; + }); +} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 4f13fbdf6a..c9c33e10c9 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -47,8 +47,8 @@ public: bool& somethingChanged) override; //angus - //virtual void update(const quint64& now) override; - //virtual bool needsToCallUpdate() const override; + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; //angus virtual void debugDump() const override; @@ -107,6 +107,8 @@ public: float getAnimationCurrentFrame() const; bool isAnimatingSomething() const; + float getCurrentlyPlayingFrame() const; + static const QString DEFAULT_TEXTURES; const QString getTextures() const; void setTextures(const QString& textures); @@ -163,6 +165,10 @@ protected: QString _textures; ShapeType _shapeType = SHAPE_TYPE_NONE; + +private: + float _currentlyPlayingFrame{ 0 }; + AnimationPropertyGroup _previousAnimationProperties; }; #endif // hifi_ModelEntityItem_h From 12e005f2157c38488e212fa53ebdfd57bb42a00e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 17 Nov 2017 17:59:11 +1300 Subject: [PATCH 052/137] Initial update of Overlays JSDoc --- interface/src/ui/overlays/Base3DOverlay.cpp | 19 ++++++++ interface/src/ui/overlays/Cube3DOverlay.cpp | 33 ++++++++++++++ interface/src/ui/overlays/Overlay.cpp | 14 ++++++ interface/src/ui/overlays/Overlays.cpp | 42 +++++++++++++++++ interface/src/ui/overlays/Overlays.h | 45 +++++++++++-------- interface/src/ui/overlays/Sphere3DOverlay.cpp | 33 ++++++++++++++ interface/src/ui/overlays/Volume3DOverlay.cpp | 5 +++ 7 files changed, 173 insertions(+), 18 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 4412014eb1..239517ece9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -200,6 +200,25 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { } } +// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. +/**jsdoc + * @typedef + * @property {string} name - TODO + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start + * @property {Vec3} localPosition - The local position of the overlay relative to its parent. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent. + * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, or + * filled. + * @property {boolean} isWire - TODO. Synonym: wire. + * @property {boolean} isDashedLine - TODO. Synonym: dashed. + * @property {boolean} ignoreRayIntersection - TODO. + * @property {boolean} drawInFront - TODO. + * @property {boolean} grabbable - TODO. + * @property {Uuid} parentID - TODO. + * @property {number} parentJointIndex - TODO. Integer. + */ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "name") { return _name; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index caa9a1b397..57b692f652 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -134,6 +134,39 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { } } +/**jsdoc + * @typedef {object} Overlays.cube3dProperties + * @property {number} borderSize - TODO + * + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * + * @property {string} name - TODO + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start + * @property {Vec3} localPosition - The local position of the overlay relative to its parent. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent. + * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, and + * filled. + * @property {boolean} isWire - TODO. Synonym: wire. + * @property {boolean} isDashedLine - TODO. Synonym: dashed. + * @property {boolean} ignoreRayIntersection - TODO. + * @property {boolean} drawInFront - TODO. + * @property {boolean} grabbable - TODO. + * @property {Uuid} parentID - TODO. + * @property {number} parentJointIndex - TODO. Integer. + * + * @property {string} type - TODO. + * @property {RGB} color - TODO. + * @property {number} alpha - TODO. + * @property {number} pulseMax - TODO. + * @property {number} pulseMin - TODO. + * @property {number} pulsePeriod - TODO. + * @property {number} alphaPulse - TODO. + * @property {number} colorPulse - TODO. + * @property {boolean} visible - TODO. + * @property {string} anchor - TODO. + */ QVariant Cube3DOverlay::getProperty(const QString& property) { if (property == "borderSize") { return _borderSize; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 01ad56f20e..0a4b16c899 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -101,6 +101,20 @@ void Overlay::setProperties(const QVariantMap& properties) { } } +// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. +/**jsdoc + * @typedef + * @property {string} type - TODO. + * @property {RGB} color - TODO. + * @property {number} alpha - TODO. + * @property {number} pulseMax - TODO. + * @property {number} pulseMin - TODO. + * @property {number} pulsePeriod - TODO. + * @property {number} alphaPulse - TODO. + * @property {number} colorPulse - TODO. + * @property {boolean} visible - TODO. + * @property {string} anchor - TODO. + */ QVariant Overlay::getProperty(const QString& property) { if (property == "type") { return QVariant(getType()); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fd5aae4027..b4de2ff7af 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -160,6 +160,48 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) Overlay::Pointer thisOverlay = nullptr; + /**jsdoc + *

Overlays may be one of the following types:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Value2D/3DDescription
billboard3DSynonym for image3d.
circle3d3DA circle.
cube3DA cube. Can also use a shape to create a cube.
grid3DA grid of lines in a plane.
image2DAn image.
image3d3DAn image.
line3d3DA line
model3DA model.
rectangle2DA rectangle.
rectangle3d3DA rectangle.
shape3DA geometric shape, such as a cube, sphere, or cylinder.
sphere3DA sphere. Can also use a shape to create a sphere.
text2DText.
text3d3DText.
web3d3DWeb content.
+ * @typedef {string} Overlays.OverlayType + */ + + /**jsdoc + *

Different overlay types have different properties:

+ * + * + * + * + * + * + * + * + * + *
Overlay TypeOverlay Properties
cube3d{@link Overlays.cube3dProperties|cube3dProperties}
sphere3d{@link Overlays.sphere3dProperties|sphere3dProperties}
TODOTODO
+ * @typedef {object} Overlays.OverlayProperties + */ + if (type == ImageOverlay::TYPE) { thisOverlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 988b288741..c51f395e53 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -45,12 +45,13 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); /**jsdoc - * @typedef Overlays.RayToOverlayIntersectionResult - * @property {bool} intersects True if the PickRay intersected with a 3D overlay. - * @property {Overlays.OverlayID} overlayID The ID of the overlay that was intersected with. - * @property {float} distance The distance from the PickRay origin to the intersection point. - * @property {Vec3} surfaceNormal The normal of the surface that was intersected with. - * @property {Vec3} intersection The point at which the PickRay intersected with the overlay. + * @typedef {object} Overlays.RayToOverlayIntersectionResult + * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise + * false. TODO: Only 3D overlay, really? What about the other properties? + * @property {Uuid} overlayID - The UUID of the overlay that was intersected with. + * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. + * @property {Vec3} surfaceNormal - The normal of the surface that was intersected with. + * @property {Vec3} intersection - The point at which the {@link PickRay} intersected with the overlay. */ class RayToOverlayIntersectionResult { public: @@ -70,13 +71,10 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value); /**jsdoc - * @typedef {int} Overlays.OverlayID - */ - -/**jsdoc - * - * Overlays namespace... + * The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to + * yourself and that aren't persisted to the domain. They are primarily used for UI. * @namespace Overlays + * @property {Uuid} keyboardFocusOverlay - Get or set the [web3d]{@link Overlays.OverlayType} overlay that has keyboard focus. */ class Overlays : public QObject { @@ -116,13 +114,11 @@ public: public slots: /**jsdoc - * Add an overlays to the scene. The properties specified will depend - * on the type of overlay that is being created. - * + * Add an overlay to the scene. * @function Overlays.addOverlay - * @param {string} type The type of the overlay to add. - * @param {Overlays.OverlayProperties} The properties of the overlay that you want to add. - * @return {Overlays.OverlayID} The ID of the newly created overlay. + * @param {Overlays.OverlayType} type - The type of the overlay to add. + * @param {Overlays.OverlayProperties} properties - The properties of the overlay to add. + * @return {Uuid} The UUID of the newly created overlay. */ OverlayID addOverlay(const QString& type, const QVariant& properties); @@ -307,7 +303,20 @@ public slots: void sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event); void sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + /**jsdoc + * Get the ID of the Web3D overlay that has keyboard focus. + * @function Overlays.getKeyboardFocusOverlay + * @returns {Uuid} The ID of the [web3d]{@link Overlays.OverlayType} overlay that has focus, if any, otherwise + * null. + */ OverlayID getKeyboardFocusOverlay(); + + /**jsdoc + * Set the ID of the overlay that has keyboard focus. + * @function Overlays.setKeyboardFocusOverlay + * @param {Uuid} id - The ID of the [web3d]{@link Overlays.OverlayType} overlay to set keyboard focus to. Use + * {@link Uuid|Uuid.NULL} or null to unset keyboard focus from an overlay. + */ void setKeyboardFocusOverlay(const OverlayID& id); signals: diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 2013e5689a..2403dc16f0 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -26,6 +26,39 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : { } +/**jsdoc +* @typedef {object} Overlays.sphere3dProperties +* +* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. +* +* @property {string} name - TODO +* @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and +* start +* @property {Vec3} localPosition - The local position of the overlay relative to its parent. +* @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. +* @property {Quat} localRotation - The orientation of the overlay relative to its parent. +* @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, and +* filled. +* @property {boolean} isWire - TODO. Synonym: wire. +* @property {boolean} isDashedLine - TODO. Synonym: dashed. +* @property {boolean} ignoreRayIntersection - TODO. +* @property {boolean} drawInFront - TODO. +* @property {boolean} grabbable - TODO. +* @property {Uuid} parentID - TODO. +* @property {number} parentJointIndex - TODO. Integer. +* +* @property {string} type - TODO. +* @property {RGB} color - TODO. +* @property {number} alpha - TODO. +* @property {number} pulseMax - TODO. +* @property {number} pulseMin - TODO. +* @property {number} pulsePeriod - TODO. +* @property {number} alphaPulse - TODO. +* @property {number} colorPulse - TODO. +* @property {boolean} visible - TODO. +* @property {string} anchor - TODO. +*/ + void Sphere3DOverlay::render(RenderArgs* args) { if (!_renderVisible) { return; // do nothing if we're not visible diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index e5a418cce5..3d7f090f82 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -61,6 +61,11 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) { } } +// JSDoc for copying to @typedefs of overlay types that inherit Volume3DOverlay. +/**jsdoc + * @typedef + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + */ QVariant Volume3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { return vec3toVariant(getDimensions()); From b246c479e36d2d5ee4addbba037780d6242e9425 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 11:25:15 +0100 Subject: [PATCH 053/137] Much better cascade blend --- .../render-utils/src/DebugDeferredBuffer.cpp | 20 +++--- libraries/render-utils/src/LightStage.cpp | 18 ++--- libraries/render-utils/src/LightStage.h | 2 - .../render-utils/src/RenderShadowTask.cpp | 7 +- libraries/render-utils/src/Shadow.slh | 21 +++--- libraries/render-utils/src/ShadowCore.slh | 68 ++++++++++--------- libraries/render-utils/src/Shadows_shared.slh | 13 ++-- 7 files changed, 74 insertions(+), 75 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 3902f4d9d4..e2d0838e27 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -151,21 +151,23 @@ static const std::string DEFAULT_SHADOW_SHADER{ }; static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ - "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );" + "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(1) );" "vec4 getFragmentColor() {" " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" " vec4 viewPosition = vec4(frag.position.xyz, 1.0);" + " float viewDepth = -viewPosition.z;" " vec4 worldPosition = getViewInverse() * viewPosition;" - " vec4 cascadeShadowCoords[4] = vec4[4](" - " evalShadowTexcoord(0, worldPosition)," - " evalShadowTexcoord(1, worldPosition)," - " evalShadowTexcoord(2, worldPosition)," - " evalShadowTexcoord(3, worldPosition)" - " );" + " vec4 cascadeShadowCoords[2];" " ivec2 cascadeIndices;" - " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" - " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" + " float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);" + " vec3 firstCascadeColor = cascadeColors[cascadeIndices.x];" + " vec3 secondCascadeColor = cascadeColors[cascadeIndices.x];" + " if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {" + " secondCascadeColor = cascadeColors[cascadeIndices.y];" + " }" + " vec3 color = mix(firstCascadeColor, secondCascadeColor, cascadeMix);" + " return vec4(mix(vec3(0.0), color, evalShadowFalloff(viewDepth)), 1.0);" "}" }; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 2c4869a9f1..38bbc2ceb9 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -35,6 +35,9 @@ LightStage::Shadow::Schema::Schema() { std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; + invCascadeBlendWidth = 1.0f / 0.1f; + invFalloffDistance = 1.0f / 2.0f; + maxDistance = 20.0f; } LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { @@ -52,11 +55,10 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); - Schema schema; + schema.cascadeCount = cascadeCount; _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); _cascades.resize(cascadeCount); - _schemaBuffer.edit().cascadeCount = cascadeCount; } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, @@ -125,12 +127,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->calculate(); // Update the buffer - auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; - schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); - schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance; - schemaCascade.maxDistance = viewMaxShadowDistance; - cascade.minDistance = viewMinShadowDistance; - cascade.maxDistance = viewMaxShadowDistance; + auto& schema = _schemaBuffer.edit(); + if (cascadeIndex == getCascadeCount() - 1) { + schema.maxDistance = viewMaxShadowDistance; + schema.invFalloffDistance = 1.0f / viewOverlapDistance; + } + schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 0b59ebdb7d..0a0a67f591 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -56,8 +56,6 @@ public: gpu::FramebufferPointer framebuffer; gpu::TexturePointer map; - float minDistance; - float maxDistance; const std::shared_ptr& getFrustum() const { return _frustum; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b9a7a99837..fef5cdddbc 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -258,7 +258,6 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto nearClip = args->getViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); - const auto nearDepth = -args->_boomOffset.z; static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; @@ -266,7 +265,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O float shadowOverlapDistance = 0.0f; if (globalShadow->getCascadeCount() > 1) { - static const float LOW_CASCADE_MAX_DISTANCE = 2.0f; + static const float LOW_CASCADE_MAX_DISTANCE = 3.0f; const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1)); maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex); @@ -276,9 +275,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { - minCascadeDistance = nearDepth; + minCascadeDistance = nearClip; } - minCascadeDistance = std::max(minCascadeDistance, nearDepth); + minCascadeDistance = std::max(minCascadeDistance, nearClip); maxCascadeDistance = std::min(maxCascadeDistance, farClip); globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 036f1029e6..c4b7828688 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -84,9 +84,7 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve } float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { - if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || - shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || - shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { + if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } @@ -95,21 +93,18 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); - vec4 cascadeShadowCoords[4] = vec4[4] ( - evalShadowTexcoord(0, worldPosition), - evalShadowTexcoord(1, worldPosition), - evalShadowTexcoord(2, worldPosition), - evalShadowTexcoord(3, worldPosition) - ); + vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; - float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices); + float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0]); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1]); } - return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); + float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); + // Falloff to max distance + return mix(1.0, attenuation, evalShadowFalloff(viewDepth)); } <@endif@> diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index e548af6a79..8ad972316a 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -21,12 +21,12 @@ int getShadowCascadeCount() { return shadow.cascadeCount; } -float getShadowCascadeInvTransitionWidth(int cascadeIndex) { - return shadow.cascades[cascadeIndex].invTransitionWidth; +float getShadowCascadeInvBlendWidth() { + return shadow.invCascadeBlendWidth; } -float getShadowCascadeMaxDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].maxDistance; +float evalShadowFalloff(float depth) { + return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0); } mat4 getShadowReprojection(int cascadeIndex) { @@ -48,44 +48,46 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); } -int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { +bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { + bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0)); + bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1)); + return all(greaterThanZero) && all(lessThanOne); +} + +int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) { int cascadeIndex; - for (cascadeIndex=0 ; cascadeIndex diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 7230ba6134..2797f4e962 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,17 +13,18 @@ struct ShadowTransform { MAT4 reprojection; float bias; - float maxDistance; - float invTransitionWidth; - float _padding; + float _padding1; + float _padding2; + float _padding3; }; struct ShadowParameters { + ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; int cascadeCount; float invMapSize; - float _padding1; - float _padding2; - ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; + float invCascadeBlendWidth; + float maxDistance; + float invFalloffDistance; }; // <@if 1@> From d98dfff0a840d4c4392185b66c067b45edbb4618 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 19:25:21 +0100 Subject: [PATCH 054/137] Still working on solving that underestimated shadow far plane on the first cascade. I have finally understood the problem (see TODO in LightStage) --- libraries/render-utils/src/LightStage.cpp | 5 +- .../render-utils/src/RenderDeferredTask.cpp | 4 +- .../render-utils/src/RenderShadowTask.cpp | 9 +- libraries/render-utils/src/ShadowCore.slh | 4 +- libraries/render/src/render/DrawTask.cpp | 185 +++++++++++++----- libraries/render/src/render/DrawTask.h | 54 +++-- libraries/shared/src/GeometryUtil.cpp | 2 +- scripts/developer/utilities/render/shadow.qml | 29 ++- 8 files changed, 212 insertions(+), 80 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 38bbc2ceb9..e9cc7d93b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -116,9 +116,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.bottomRight); fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); + + // TODO: Far distance should be extended to the intersection of the exteruded shadow frustum far plane + // with the view frustum. // Re-adjust near shadow distance - auto near = glm::max(max.z, -nearDepth); + auto near = glm::min(-max.z, nearDepth); auto far = -min.z; glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); cascade._frustum->setProjection(ortho); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d2606bf42f..8bd5ae8447 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -204,7 +204,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren for (auto i = 0; i < ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT; i++) { const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM+i); float tint = 1.0f - i / float(ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT - 1); - task.addJob("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); + char jobName[64]; + sprintf(jobName, "DrawShadowFrustum%d", i); + task.addJob(jobName, shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); } // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index fef5cdddbc..6b00a14d23 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -112,8 +112,7 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum float far = 0.0f; computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far); - // Limit the far range to the one used originally. There's no point in rendering objects - // that are not in the view frustum. + // Limit the far range to the one used originally. far = glm::min(far, shadowFrustum.getFarClip()); const auto depthEpsilon = 0.1f; @@ -233,7 +232,6 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", setupOutput); } } @@ -272,12 +270,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O minCascadeDistance = maxCascadeDistance / cascadeLevelScale; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 4.0f; maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearClip; + } else { + minCascadeDistance = std::max(minCascadeDistance, nearClip); } - minCascadeDistance = std::max(minCascadeDistance, nearClip); maxCascadeDistance = std::min(maxCascadeDistance, farClip); globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 8ad972316a..0a34cafe6f 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -49,8 +49,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { - bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0)); - bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1)); + bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0)); + bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0f4137e38d..629cc55ccb 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -215,79 +215,158 @@ void DrawBounds::run(const RenderContextPointer& renderContext, }); } -gpu::PipelinePointer DrawFrustum::_pipeline; +DrawQuadVolume::DrawQuadVolume(const glm::vec3& color) : + _color{ color } { + _meshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); + _meshStream.addBuffer(_meshVertices._buffer, _meshVertices._offset, _meshVertices._stride); +} + +void DrawQuadVolume::configure(const Config& configuration) { + _isUpdateEnabled = !configuration.isFrozen; +} + +void DrawQuadVolume::run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount) { + assert(renderContext->args); + assert(renderContext->args->_context); + if (_isUpdateEnabled) { + auto& streamVertices = _meshVertices.edit >(); + std::copy(vertices, vertices + 8, streamVertices.begin()); + } + + RenderArgs* args = renderContext->args; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(getPipeline()); + batch.setIndexBuffer(indices); + + batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); + batch.setInputStream(0, _meshStream); + batch.drawIndexed(gpu::LINES, indexCount, 0U); + + args->_batch = nullptr; + }); +} + +gpu::PipelinePointer DrawQuadVolume::getPipeline() { + static gpu::PipelinePointer pipeline; + + if (!pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); + auto ps = gpu::StandardShaderLib::getDrawColorPS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("color", 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(true, false)); + pipeline = gpu::Pipeline::create(program, state); + } + return pipeline; +} + +gpu::BufferView DrawAABox::_cubeMeshIndices; + +DrawAABox::DrawAABox(const glm::vec3& color) : + DrawQuadVolume{ color } { +} + +void DrawAABox::run(const render::RenderContextPointer& renderContext, const Inputs& box) { + if (!box.isNull()) { + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; + + if (!_cubeMeshIndices._buffer) { + auto indices = std::make_shared(sizeof(indexData), indexData); + _cubeMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); + } + + glm::vec3 vertices[8]; + + getVertices(box, vertices); + + DrawQuadVolume::run(renderContext, vertices, _cubeMeshIndices, sizeof(indexData) / sizeof(indexData[0])); + } +} + +void DrawAABox::getVertices(const AABox& box, glm::vec3 vertices[8]) { + vertices[0] = box.getVertex(TOP_LEFT_NEAR); + vertices[1] = box.getVertex(TOP_RIGHT_NEAR); + vertices[2] = box.getVertex(BOTTOM_RIGHT_NEAR); + vertices[3] = box.getVertex(BOTTOM_LEFT_NEAR); + vertices[4] = box.getVertex(TOP_LEFT_FAR); + vertices[5] = box.getVertex(TOP_RIGHT_FAR); + vertices[6] = box.getVertex(BOTTOM_RIGHT_FAR); + vertices[7] = box.getVertex(BOTTOM_LEFT_FAR); +} + gpu::BufferView DrawFrustum::_frustumMeshIndices; DrawFrustum::DrawFrustum(const glm::vec3& color) : - _color{ color } { - _frustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); - _frustumMeshStream.addBuffer(_frustumMeshVertices._buffer, _frustumMeshVertices._offset, _frustumMeshVertices._stride); -} - -void DrawFrustum::configure(const Config& configuration) { - _updateFrustum = !configuration.isFrozen; + DrawQuadVolume{ color } { } void DrawFrustum::run(const render::RenderContextPointer& renderContext, const Input& input) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* args = renderContext->args; if (input) { const auto& frustum = *input; - static uint8_t indexData[] = { 0, 1, 2, 3, 0, 4, 5, 6, 7, 4, 5, 1, 2, 6, 7, 3 }; + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 0, 2, + 3, 1, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 4, 6, + 7, 5, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; if (!_frustumMeshIndices._buffer) { auto indices = std::make_shared(sizeof(indexData), indexData); _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); } - if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); - auto ps = gpu::StandardShaderLib::getDrawColorPS(); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + glm::vec3 vertices[8]; - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("color", 0)); - gpu::Shader::makeProgram(*program, slotBindings); + getVertices(frustum, vertices); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(true, false)); - _pipeline = gpu::Pipeline::create(program, state); - } - - if (_updateFrustum) { - updateFrustum(frustum); - } - - // Render the frustums in wireframe - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setPipeline(_pipeline); - batch.setIndexBuffer(_frustumMeshIndices); - - batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); - batch.setInputStream(0, _frustumMeshStream); - batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); - - args->_batch = nullptr; - }); + DrawQuadVolume::run(renderContext, vertices, _frustumMeshIndices, sizeof(indexData) / sizeof(indexData[0])); } } -void DrawFrustum::updateFrustum(const ViewFrustum& frustum) { - auto& vertices = _frustumMeshVertices.edit >(); +void DrawFrustum::getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]) { vertices[0] = frustum.getNearTopLeft(); vertices[1] = frustum.getNearTopRight(); vertices[2] = frustum.getNearBottomRight(); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 5d98c37c21..6f98e3bef1 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -70,12 +70,12 @@ private: int _colorLocation { -1 }; }; -class DrawFrustumConfig : public render::JobConfig { +class DrawQuadVolumeConfig : public render::JobConfig { Q_OBJECT Q_PROPERTY(bool isFrozen MEMBER isFrozen NOTIFY dirty) public: - DrawFrustumConfig(bool enabled = false) : JobConfig(enabled) {} + DrawQuadVolumeConfig(bool enabled = false) : JobConfig(enabled) {} bool isFrozen{ false }; signals: @@ -83,30 +83,58 @@ signals: }; -class DrawFrustum { +class DrawQuadVolume { +public: + + using Config = DrawQuadVolumeConfig; + + void configure(const Config& configuration); + +protected: + DrawQuadVolume(const glm::vec3& color); + + void run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount); + + gpu::BufferView _meshVertices; + gpu::BufferStream _meshStream; + glm::vec3 _color; + bool _isUpdateEnabled{ true }; + + static gpu::PipelinePointer getPipeline(); +}; + +class DrawAABox : public DrawQuadVolume { +public: + using Inputs = AABox; + using JobModel = render::Job::ModelI; + + DrawAABox(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); + + void run(const render::RenderContextPointer& renderContext, const Inputs& box); + +protected: + + static gpu::BufferView _cubeMeshIndices; + + static void getVertices(const AABox& box, glm::vec3 vertices[8]); +}; + +class DrawFrustum : public DrawQuadVolume { public: - using Config = DrawFrustumConfig; using Input = ViewFrustumPointer; using JobModel = render::Job::ModelI; DrawFrustum(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); - void configure(const Config& configuration); void run(const render::RenderContextPointer& renderContext, const Input& input); private: - static gpu::PipelinePointer _pipeline; static gpu::BufferView _frustumMeshIndices; - bool _updateFrustum{ true }; - gpu::BufferView _frustumMeshVertices; - gpu::BufferStream _frustumMeshStream; - glm::vec3 _color; - - void updateFrustum(const ViewFrustum& frustum); + static void getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]); }; - } #endif // hifi_render_DrawTask_h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 2ee761a1f7..956c61deaf 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -422,7 +422,7 @@ int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int pl *clippedTriangles = triangle; - while (planes < planesEnd) { + while (planes < planesEnd && triangleCount) { int clippedSubTriangleCount; trianglesToTest.clear(); diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 8548ba4119..a8fcf1aec7 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -15,15 +15,24 @@ Column { id: root spacing: 8 property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum"); - property var shadowConfig: Render.getConfig("RenderMainView.DrawShadowFrustum"); + property var shadow0Config: Render.getConfig("RenderMainView.DrawShadowFrustum0"); + property var shadow1Config: Render.getConfig("RenderMainView.DrawShadowFrustum1"); + property var shadow2Config: Render.getConfig("RenderMainView.DrawShadowFrustum2"); + property var shadow3Config: Render.getConfig("RenderMainView.DrawShadowFrustum3"); Component.onCompleted: { viewConfig.enabled = true; - shadowConfig.enabled = true; + shadow0Config.enabled = true; + shadow1Config.enabled = true; + shadow2Config.enabled = true; + shadow3Config.enabled = true; } Component.onDestruction: { viewConfig.enabled = false; - shadowConfig.enabled = false; + shadow0Config.enabled = false; + shadow1Config.enabled = false; + shadow2Config.enabled = false; + shadow3Config.enabled = false; } CheckBox { @@ -31,7 +40,14 @@ Column { checked: false onCheckedChanged: { viewConfig.isFrozen = checked; - shadowConfig.isFrozen = checked; + shadow0Config.isFrozen = checked; + shadow1Config.isFrozen = checked; + shadow2Config.isFrozen = checked; + shadow3Config.isFrozen = checked; + shadow0BoundConfig.isFrozen = checked; + shadow1BoundConfig.isFrozen = checked; + shadow2BoundConfig.isFrozen = checked; + shadow3BoundConfig.isFrozen = checked; } } Row { @@ -46,5 +62,10 @@ Column { color: "blue" font.italic: true } + Label { + text: "Items" + color: "magenta" + font.italic: true + } } } From 013d16cee9e0d30c05458e76685ae8675549efba Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 17 Nov 2017 18:37:54 -0800 Subject: [PATCH 055/137] this is the change to add a property for the currentframeplaying --- .../src/RenderableModelEntityItem.cpp | 27 +++++++-- .../src/RenderableModelEntityItem.h | 4 +- .../entities/src/AnimationPropertyGroup.cpp | 16 +++++ .../entities/src/AnimationPropertyGroup.h | 1 + .../entities/src/EntityItemProperties.cpp | 1 + libraries/entities/src/EntityPropertyFlags.h | 2 + libraries/entities/src/ModelEntityItem.cpp | 60 +++++++++++++++---- libraries/entities/src/ModelEntityItem.h | 11 +++- 8 files changed, 102 insertions(+), 20 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2ba8a7a45f..3176010536 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -993,12 +993,19 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { bool isHolding = entity->getAnimationHold(); int updatedFrameCount = frameCount; + //get the updated frame from the ModelEntity + auto modelAnimProperties = entity->getAnimationProperties(); + withWriteLock([&] { + _currentFrame = modelAnimProperties.getCurrentlyPlayingFrame(); + }); + qCDebug(entitiesrenderer) << "the client frame count is the following " << _currentFrame; + if ((firstFrame >= 0) && (firstFrame < lastFrame) && (lastFrame <= frameCount)) { //length of animation in now determined by first and last frame updatedFrameCount = (lastFrame - firstFrame + 1); } - + /* if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; @@ -1008,7 +1015,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto interval = now - _lastAnimated; _lastAnimated = now; - + //here we implement the looping animation property //if we have played through the animation once then we hold on the last frame @@ -1021,12 +1028,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); } } - + */ { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentFrame - _renderAnimationProperties.getFirstFrame())) % updatedFrameCount; + int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; //this gives us the absolute frame value to use by adding the first frame value. - animationCurrentFrame += _renderAnimationProperties.getFirstFrame(); + animationCurrentFrame += firstFrame; @@ -1347,7 +1354,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (model->getRenderItemsNeedUpdate()) { model->updateRenderItems(); } - + /* { DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); // make a copy of the animation properites @@ -1370,6 +1377,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce }); } } + */ + //angus + { + + //_currentFrame = entity->getCurrentlyPlayingFrame(); + } + //angus + if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 16892ce3e0..3a0c5c506b 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -183,9 +183,9 @@ private: bool _marketplaceEntity { false }; bool _shouldHighlight { false }; bool _animating { false }; - uint64_t _lastAnimated { 0 }; + //uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; - float _endAnim{ 0 }; + //float _endAnim{ 0 }; }; diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index c871849064..5f5d68be24 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -53,6 +53,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, Animation, animation, CurrentlyPlayingFrame, currentlyPlayingFrame); } @@ -73,6 +74,9 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); + //angus added + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentlyPlayingFrame, float, setCurrentlyPlayingFrame); + // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, setRunning, getRunning); @@ -89,6 +93,7 @@ void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(firstFrame); COPY_PROPERTY_IF_CHANGED(lastFrame); COPY_PROPERTY_IF_CHANGED(hold); + COPY_PROPERTY_IF_CHANGED(currentlyPlayingFrame); } void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { @@ -195,6 +200,8 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + //angus + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, getCurrentlyPlayingFrame()); return true; } @@ -216,6 +223,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, float, setCurrentlyPlayingFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); @@ -226,6 +234,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame); processedBytes += bytesRead; @@ -258,6 +267,7 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, currentlyPlayingFrame); return changedProperties; } @@ -272,6 +282,7 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentlyPlayingFrame, getCurrentlyPlayingFrame); } bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -286,6 +297,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentlyPlayingFrame, currentlyPlayingFrame, setCurrentlyPlayingFrame); return somethingChanged; } @@ -301,6 +313,8 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_HOLD; requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION; + //angus + requestedProperties += PROP_ANIMATION_CURRENTLY_PLAYING_FRAME; return requestedProperties; } @@ -324,6 +338,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, getCurrentlyPlayingFrame()); } int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -343,5 +358,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, float, setCurrentlyPlayingFrame); return bytesRead; } diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 54d4ced92f..8d0087b56d 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -86,6 +86,7 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); + DEFINE_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame, currentlyPlayingFrame, float, 0.0f); protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 108fc14e30..77fa1231f1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1148,6 +1148,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, Animation, animation, CurrentlyPlayingFrame, currentlyPlayingFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 35d40b669a..3b1f4d60d0 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -40,6 +40,8 @@ enum EntityPropertyList { PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_PLAYING, PROP_ANIMATION_ALLOW_TRANSLATION, + //angus + PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index c24efa8eec..67357a6905 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -199,19 +199,20 @@ void ModelEntityItem::update(const quint64& now) { if (_previousAnimationProperties != currentAnimationProperties) { qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code"; withWriteLock([&] { - //if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { - // if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { - // _currentFrame = newAnimationProperties.getCurrentFrame(); - // _endAnim = _currentFrame + ( newAnimationProperties.getLastFrame() - newAnimationProperties.getFirstFrame() ); - //_lastAnimated = 0; - // } - //}else if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { - // int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); - // _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; - // } + if ( (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) || (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + if (!(currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getLastFrame()) && !(currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getFirstFrame())) { + _currentlyPlayingFrame = currentAnimationProperties.getCurrentFrame(); + _endAnim = _currentlyPlayingFrame + ( currentAnimationProperties.getLastFrame() - currentAnimationProperties.getFirstFrame() ); + _lastAnimated = 0; + } + }else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { + int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); + _endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + } _previousAnimationProperties = currentAnimationProperties; }); } + updateFrameCount(); } @@ -225,6 +226,38 @@ bool ModelEntityItem::needsToCallUpdate() const { return true; } +void ModelEntityItem::updateFrameCount() { + + + if (!_lastAnimated) { + _lastAnimated = usecTimestampNow(); + return; + } + + auto now = usecTimestampNow(); + auto interval = now - _lastAnimated; + _lastAnimated = now; + + + //here we implement the looping animation property + //get entity anim props + bool isLooping = getAnimationLoop(); + int firstFrame = getAnimationFirstFrame(); + int lastFrame = getAnimationLastFrame(); + bool isHolding = getAnimationHold(); + + if (isLooping || (_currentlyPlayingFrame < _endAnim)) { + //else advance the current frame. + //if hold or not playing don't advance the current frame. + //also if the animFrame is outside of first or last frame then don't advance the motion. + if (!isHolding && getAnimationIsPlaying() && !(_previousAnimationProperties.getCurrentFrame() > _previousAnimationProperties.getLastFrame()) && !(_previousAnimationProperties.getCurrentFrame() < _previousAnimationProperties.getFirstFrame())) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentlyPlayingFrame += (deltaTime * _previousAnimationProperties.getFPS()); + qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; + } + } +} + //angus @@ -650,3 +683,10 @@ float ModelEntityItem::getCurrentlyPlayingFrame() const { return _currentlyPlayingFrame; }); } + +int ModelEntityItem::getLastKnownCurrentFrame() const { + return resultWithReadLock([&] { + return _lastKnownCurrentFrame; + }); +} +//angus change \ No newline at end of file diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index c9c33e10c9..3df0ccdc36 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -17,6 +17,7 @@ #include #include "AnimationPropertyGroup.h" + class ModelEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -49,6 +50,7 @@ public: //angus virtual void update(const quint64& now) override; virtual bool needsToCallUpdate() const override; + void updateFrameCount(); //angus virtual void debugDump() const override; @@ -108,6 +110,7 @@ public: bool isAnimatingSomething() const; float getCurrentlyPlayingFrame() const; + int getLastKnownCurrentFrame() const; static const QString DEFAULT_TEXTURES; const QString getTextures() const; @@ -152,7 +155,7 @@ protected: }; QVector _localJointData; - int _lastKnownCurrentFrame; + int _lastKnownCurrentFrame{-1}; rgbColor _color; QString _modelURL; @@ -167,8 +170,12 @@ protected: ShapeType _shapeType = SHAPE_TYPE_NONE; private: - float _currentlyPlayingFrame{ 0 }; + //angus + float _currentlyPlayingFrame{ -1 }; + float _endAnim{ 0 }; + uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; + //angus }; #endif // hifi_ModelEntityItem_h From b4bc8da20af9eda4a2f95994dfaa1d16bb9695fb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 19 Nov 2017 08:44:00 +1300 Subject: [PATCH 056/137] Document deprecation of "filed" property --- interface/src/ui/overlays/Base3DOverlay.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 239517ece9..6e921bbc81 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -209,9 +209,10 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { * @property {Vec3} localPosition - The local position of the overlay relative to its parent. * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent. - * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, or - * filled. - * @property {boolean} isWire - TODO. Synonym: wire. + * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, + * filled, or filed. Deprecated: The erroneous property spelling + * "filed" is deprecated and support for it will be removed shortly. + * @property {boolean} isWire - TODO. Synonym: wire. TODO: Actuall, this is an antonym?! * @property {boolean} isDashedLine - TODO. Synonym: dashed. * @property {boolean} ignoreRayIntersection - TODO. * @property {boolean} drawInFront - TODO. From befa0f8ab54de74211b85361565c86bd9351f138 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 20 Nov 2017 09:11:15 -0800 Subject: [PATCH 057/137] now the update frame is only happening in the assignment client code. added setCurrentlyPlayingFrame to ModelEntityItem.cpp --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 3 ++- libraries/entities/src/ModelEntityItem.cpp | 8 ++++++++ libraries/entities/src/ModelEntityItem.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 07d8716656..42fcd9af6f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -309,7 +309,8 @@ void EntityTreeRenderer::update(bool simulate) { PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); - tree->update(simulate); + //angus + //tree->update(simulate); // Update the rendereable entities as needed { diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 67357a6905..c6e6a7a069 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -254,6 +254,7 @@ void ModelEntityItem::updateFrameCount() { float deltaTime = (float)interval / (float)USECS_PER_SECOND; _currentlyPlayingFrame += (deltaTime * _previousAnimationProperties.getFPS()); qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; + setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); } } } @@ -609,6 +610,12 @@ void ModelEntityItem::setAnimationCurrentFrame(float value) { }); } +void ModelEntityItem::setAnimationCurrentlyPlayingFrame(float value) { + withWriteLock([&] { + _animationProperties.setCurrentlyPlayingFrame(value); + }); +} + void ModelEntityItem::setAnimationLoop(bool loop) { withWriteLock([&] { _animationProperties.setLoop(loop); @@ -669,6 +676,7 @@ float ModelEntityItem::getAnimationCurrentFrame() const { return _animationProperties.getCurrentFrame(); }); } + //angus change bool ModelEntityItem::isAnimatingSomething() const { return resultWithReadLock([&] { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 3df0ccdc36..1c1c834d82 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -87,6 +87,7 @@ public: void setAnimationURL(const QString& url); void setAnimationCurrentFrame(float value); + void setAnimationCurrentlyPlayingFrame(float value); void setAnimationIsPlaying(bool value); void setAnimationFPS(float value); From 8e647fb32c22100f51d1125c5d1a51651bf31e1c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Nov 2017 11:00:36 +1300 Subject: [PATCH 058/137] Disable unused Overlays.panelDeleted signal #if'd out to match the other panel code that is disabled. --- interface/src/ui/overlays/Overlays.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index c51f395e53..4554914bb4 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -327,7 +327,10 @@ signals: * @param {OverlayID} The ID of the overlay that was deleted. */ void overlayDeleted(OverlayID id); + +#if OVERLAY_PANELS void panelDeleted(OverlayID id); +#endif void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event); From 1e5d099b06feaac93e092a4d5e70311b87258714 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 21 Nov 2017 17:36:01 -0800 Subject: [PATCH 059/137] changed the currentlyplayingframe property so that it is updated in RenderableModelEntity.cpp now. Remains to remove everything but initializing currentlyPlayingFrame in ModelEntityRender.cpp --- .../src/RenderableModelEntityItem.cpp | 30 +++++++++---------- .../src/RenderableModelEntityItem.h | 7 +++-- .../entities/src/AnimationPropertyGroup.cpp | 9 ++++++ libraries/entities/src/ModelEntityItem.cpp | 5 +++- libraries/entities/src/ModelEntityItem.h | 2 ++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3176010536..a3e90c925f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -995,9 +995,13 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { //get the updated frame from the ModelEntity auto modelAnimProperties = entity->getAnimationProperties(); - withWriteLock([&] { - _currentFrame = modelAnimProperties.getCurrentlyPlayingFrame(); - }); + + + //_currentFrame = modelAnimProperties.getCurrentlyPlayingFrame(); + + //tempbool = modelAnimProperties.getRunning(); + //qCDebug(entitiesrenderer) << "is playing is: " << tempbool; + qCDebug(entitiesrenderer) << "the client frame count is the following " << _currentFrame; if ((firstFrame >= 0) && (firstFrame < lastFrame) && (lastFrame <= frameCount)) { @@ -1005,7 +1009,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { updatedFrameCount = (lastFrame - firstFrame + 1); } - /* + if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; @@ -1025,13 +1029,13 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { //also if the animFrame is outside of first or last frame then don't advance the motion. if (!isHolding && entity->getAnimationIsPlaying() && !( _renderAnimationProperties.getCurrentFrame() > _renderAnimationProperties.getLastFrame() ) && !( _renderAnimationProperties.getCurrentFrame() < _renderAnimationProperties.getFirstFrame() ) ) { float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + _currentlyPlayingFrame += (deltaTime * _renderAnimationProperties.getFPS()); } } - */ + { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; + int animationCurrentFrame = (int)(glm::floor(_currentlyPlayingFrame - firstFrame)) % updatedFrameCount; //this gives us the absolute frame value to use by adding the first frame value. animationCurrentFrame += firstFrame; @@ -1354,7 +1358,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (model->getRenderItemsNeedUpdate()) { model->updateRenderItems(); } - /* + { DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); // make a copy of the animation properites @@ -1373,17 +1377,13 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; } + _currentlyPlayingFrame = newAnimationProperties.getCurrentlyPlayingFrame(); _renderAnimationProperties = newAnimationProperties; }); } } - */ - //angus - { - - //_currentFrame = entity->getCurrentlyPlayingFrame(); - } - //angus + //angus + if (_animating) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 3a0c5c506b..e6a970d0eb 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -183,10 +183,11 @@ private: bool _marketplaceEntity { false }; bool _shouldHighlight { false }; bool _animating { false }; - //uint64_t _lastAnimated { 0 }; + uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; - //float _endAnim{ 0 }; - + float _endAnim{ 0 }; + bool tempbool{ false }; + float _currentlyPlayingFrame{ 0 }; }; } } // namespace diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 5f5d68be24..8488c6fb25 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -109,6 +109,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { bool loop = getLoop(); bool hold = getHold(); bool allowTranslation = getAllowTranslation(); + float currentlyPlayingFrame = getCurrentlyPlayingFrame(); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonObject settingsAsJsonObject = settingsAsJson.object(); @@ -147,6 +148,10 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { allowTranslation = settingsMap["allowTranslation"].toBool(); } + if (settingsMap.contains("currentlyPlayingFrame")) { + currentlyPlayingFrame = settingsMap["currentlyPlayingFrame"].toFloat(); + } + setAllowTranslation(allowTranslation); setFPS(fps); setCurrentFrame(currentFrame); @@ -155,6 +160,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { setLastFrame(lastFrame); setLoop(loop); setHold(hold); + setCurrentlyPlayingFrame(currentlyPlayingFrame); } @@ -179,6 +185,9 @@ void AnimationPropertyGroup::listChangedProperties(QList& out) { if (allowTranslationChanged()) { out << "animation-allowTranslation"; } + if (currentlyPlayingFrameChanged()) { + out << "animation-currentlyPlayingFrame"; + } } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index c6e6a7a069..784193d3d5 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -209,9 +209,10 @@ void ModelEntityItem::update(const quint64& now) { int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); _endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; } - _previousAnimationProperties = currentAnimationProperties; + //_previousAnimationProperties = currentAnimationProperties; }); } + _previousAnimationProperties = currentAnimationProperties; updateFrameCount(); } @@ -256,6 +257,7 @@ void ModelEntityItem::updateFrameCount() { qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); } + } } @@ -611,6 +613,7 @@ void ModelEntityItem::setAnimationCurrentFrame(float value) { } void ModelEntityItem::setAnimationCurrentlyPlayingFrame(float value) { + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; withWriteLock([&] { _animationProperties.setCurrentlyPlayingFrame(value); }); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 1c1c834d82..2e901ed318 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -176,6 +176,8 @@ private: float _endAnim{ 0 }; uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; + bool _propTestFlag{ true }; + bool _propTestFlag2{ true }; //angus }; From fdf5139a7409c231598d59e4350b85415e062243 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Nov 2017 17:52:13 -0800 Subject: [PATCH 060/137] everything now works with an atomic start time property. but now I will get rid of this and use current frame instead --- .../src/RenderableModelEntityItem.cpp | 33 ++++++++--- .../src/RenderableModelEntityItem.h | 2 +- .../entities/src/AnimationPropertyGroup.cpp | 10 ++-- .../entities/src/AnimationPropertyGroup.h | 2 +- libraries/entities/src/ModelEntityItem.cpp | 55 ++++++++++++------- libraries/entities/src/ModelEntityItem.h | 6 +- 6 files changed, 70 insertions(+), 38 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a3e90c925f..80aba9deba 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -997,7 +997,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto modelAnimProperties = entity->getAnimationProperties(); - //_currentFrame = modelAnimProperties.getCurrentlyPlayingFrame(); + //_currentFrame = modelAnimProperties.getCurrentFrame(); //tempbool = modelAnimProperties.getRunning(); //qCDebug(entitiesrenderer) << "is playing is: " << tempbool; @@ -1016,26 +1016,44 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { } auto now = usecTimestampNow(); - auto interval = now - _lastAnimated; + + //find out how long it has been since this animation started. + auto interval = now - _currentlyPlayingFrame; + //auto interval = now - _lastAnimated; _lastAnimated = now; + //new global start time code + float nowTime = (float)interval / (float)USECS_PER_SECOND; + float oldCurrentFrame = _currentFrame; + _currentFrame = _renderAnimationProperties.getCurrentFrame() + (nowTime * _renderAnimationProperties.getFPS()); + + //here we implement the looping animation property //if we have played through the animation once then we hold on the last frame - if( isLooping || ( _currentFrame < _endAnim ) ){ + if( isLooping || (_currentFrame < _endAnim ) ){ //else advance the current frame. //if hold or not playing don't advance the current frame. //also if the animFrame is outside of first or last frame then don't advance the motion. if (!isHolding && entity->getAnimationIsPlaying() && !( _renderAnimationProperties.getCurrentFrame() > _renderAnimationProperties.getLastFrame() ) && !( _renderAnimationProperties.getCurrentFrame() < _renderAnimationProperties.getFirstFrame() ) ) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentlyPlayingFrame += (deltaTime * _renderAnimationProperties.getFPS()); + //float deltaTime = (float)interval / (float)USECS_PER_SECOND; + //_currentlyPlayingFrame += (deltaTime * _renderAnimationProperties.getFPS()); + //do nothing } + else { + //use old currentFrame + _currentFrame = oldCurrentFrame; + } + } + else { + //make current frame the endanim frame + _currentFrame = _endAnim; } { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentlyPlayingFrame - firstFrame)) % updatedFrameCount; + int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; //this gives us the absolute frame value to use by adding the first frame value. animationCurrentFrame += firstFrame; @@ -1369,7 +1387,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce withWriteLock([&] { if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { - _currentFrame = newAnimationProperties.getCurrentFrame(); + _currentFrame = newAnimationProperties.getCurrentFrame();// +((float)newAnimationProperties.getCurrentlyPlayingFrame() / (float)USECS_PER_SECOND)*(newAnimationProperties.getFPS()); _endAnim = _currentFrame + ( newAnimationProperties.getLastFrame() - newAnimationProperties.getFirstFrame() ); _lastAnimated = 0; } @@ -1378,6 +1396,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; } _currentlyPlayingFrame = newAnimationProperties.getCurrentlyPlayingFrame(); + qCDebug(entitiesrenderer) << "renderable update to currently playing frame " << _currentlyPlayingFrame; _renderAnimationProperties = newAnimationProperties; }); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index e6a970d0eb..fdb0cf5d2c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -187,7 +187,7 @@ private: float _currentFrame { 0 }; float _endAnim{ 0 }; bool tempbool{ false }; - float _currentlyPlayingFrame{ 0 }; + quint64 _currentlyPlayingFrame{ 0 }; }; } } // namespace diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 8488c6fb25..67de381d1b 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -75,7 +75,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); //angus added - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentlyPlayingFrame, float, setCurrentlyPlayingFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentlyPlayingFrame, quint64, setCurrentlyPlayingFrame); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); @@ -109,7 +109,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { bool loop = getLoop(); bool hold = getHold(); bool allowTranslation = getAllowTranslation(); - float currentlyPlayingFrame = getCurrentlyPlayingFrame(); + quint64 currentlyPlayingFrame = getCurrentlyPlayingFrame(); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonObject settingsAsJsonObject = settingsAsJson.object(); @@ -149,7 +149,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { } if (settingsMap.contains("currentlyPlayingFrame")) { - currentlyPlayingFrame = settingsMap["currentlyPlayingFrame"].toFloat(); + currentlyPlayingFrame = settingsMap["currentlyPlayingFrame"].toULongLong(); } setAllowTranslation(allowTranslation); @@ -232,7 +232,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, float, setCurrentlyPlayingFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, quint64, setCurrentlyPlayingFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); @@ -367,6 +367,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, float, setCurrentlyPlayingFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, quint64, setCurrentlyPlayingFrame); return bytesRead; } diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 8d0087b56d..1a3da52684 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -86,7 +86,7 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); - DEFINE_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame, currentlyPlayingFrame, float, 0.0f); + DEFINE_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame, currentlyPlayingFrame, quint64, 0); protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 784193d3d5..bdf4f6f3f9 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -200,19 +200,23 @@ void ModelEntityItem::update(const quint64& now) { qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code"; withWriteLock([&] { if ( (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) || (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { - if (!(currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getLastFrame()) && !(currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getFirstFrame())) { - _currentlyPlayingFrame = currentAnimationProperties.getCurrentFrame(); - _endAnim = _currentlyPlayingFrame + ( currentAnimationProperties.getLastFrame() - currentAnimationProperties.getFirstFrame() ); - _lastAnimated = 0; - } - }else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { - int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); - _endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + // if (!(currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getLastFrame()) && !(currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getFirstFrame())) { + // _currentlyPlayingFrame = currentAnimationProperties.getCurrentFrame(); + //_endAnim = _currentlyPlayingFrame + ( currentAnimationProperties.getLastFrame() - currentAnimationProperties.getFirstFrame() ); + //_lastAnimated = 0; + // } + setAnimationCurrentlyPlayingFrame(usecTimestampNow()); } - //_previousAnimationProperties = currentAnimationProperties; + //else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { + // int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); + //_endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + //} + _previousAnimationProperties = currentAnimationProperties; }); + + qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, currently playing frame is: " << currentAnimationProperties.getCurrentlyPlayingFrame(); } - _previousAnimationProperties = currentAnimationProperties; + //_previousAnimationProperties = currentAnimationProperties; updateFrameCount(); } @@ -229,36 +233,45 @@ bool ModelEntityItem::needsToCallUpdate() const { void ModelEntityItem::updateFrameCount() { - + if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; } + auto now = usecTimestampNow(); - auto interval = now - _lastAnimated; - _lastAnimated = now; + + //this is now getting the time since the server started the animation. + //auto interval = now - _currentlyPlayingFrame; + //auto interval = now - _lastAnimated; + //_lastAnimated = now; + + //here we implement the looping animation property //get entity anim props + bool isLooping = getAnimationLoop(); int firstFrame = getAnimationFirstFrame(); int lastFrame = getAnimationLastFrame(); bool isHolding = getAnimationHold(); - if (isLooping || (_currentlyPlayingFrame < _endAnim)) { + //if (isLooping || (_currentFrame < _endAnim)) { //else advance the current frame. //if hold or not playing don't advance the current frame. //also if the animFrame is outside of first or last frame then don't advance the motion. if (!isHolding && getAnimationIsPlaying() && !(_previousAnimationProperties.getCurrentFrame() > _previousAnimationProperties.getLastFrame()) && !(_previousAnimationProperties.getCurrentFrame() < _previousAnimationProperties.getFirstFrame())) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentlyPlayingFrame += (deltaTime * _previousAnimationProperties.getFPS()); - qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; - setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); + // float deltaTime = (float)interval / (float)USECS_PER_SECOND; + // _currentlyPlayingFrame += (deltaTime * _previousAnimationProperties.getFPS()); + // qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; + // setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); + setAnimationCurrentlyPlayingFrame(now); } - } + //} + } //angus @@ -612,7 +625,7 @@ void ModelEntityItem::setAnimationCurrentFrame(float value) { }); } -void ModelEntityItem::setAnimationCurrentlyPlayingFrame(float value) { +void ModelEntityItem::setAnimationCurrentlyPlayingFrame(quint64 value) { _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; withWriteLock([&] { _animationProperties.setCurrentlyPlayingFrame(value); @@ -689,7 +702,7 @@ bool ModelEntityItem::isAnimatingSomething() const { }); } -float ModelEntityItem::getCurrentlyPlayingFrame() const { +quint64 ModelEntityItem::getCurrentlyPlayingFrame() const { return resultWithReadLock([&] { return _currentlyPlayingFrame; }); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2e901ed318..dad022ae0a 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -87,7 +87,7 @@ public: void setAnimationURL(const QString& url); void setAnimationCurrentFrame(float value); - void setAnimationCurrentlyPlayingFrame(float value); + void setAnimationCurrentlyPlayingFrame(quint64 value); void setAnimationIsPlaying(bool value); void setAnimationFPS(float value); @@ -110,7 +110,7 @@ public: float getAnimationCurrentFrame() const; bool isAnimatingSomething() const; - float getCurrentlyPlayingFrame() const; + quint64 getCurrentlyPlayingFrame() const; int getLastKnownCurrentFrame() const; static const QString DEFAULT_TEXTURES; @@ -172,7 +172,7 @@ protected: private: //angus - float _currentlyPlayingFrame{ -1 }; + quint64 _currentlyPlayingFrame{ 0 }; float _endAnim{ 0 }; uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; From 2e75016021d41cd4057c9c1f44a5325c2dd5c7cc Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Nov 2017 17:53:21 -0800 Subject: [PATCH 061/137] changes to modelEntity.cpp --- .../src/RenderableModelEntityItem.cpp | 46 +++++++++---------- libraries/entities/src/ModelEntityItem.cpp | 45 +++++++++++------- libraries/entities/src/ModelEntityItem.h | 1 + 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 80aba9deba..b3417e7946 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -991,7 +991,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { int firstFrame = entity->getAnimationFirstFrame(); int lastFrame = entity->getAnimationLastFrame(); bool isHolding = entity->getAnimationHold(); - int updatedFrameCount = frameCount; + int updatedFrameCount = lastFrame - firstFrame + 1; //get the updated frame from the ModelEntity auto modelAnimProperties = entity->getAnimationProperties(); @@ -1018,45 +1018,45 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto now = usecTimestampNow(); //find out how long it has been since this animation started. - auto interval = now - _currentlyPlayingFrame; - //auto interval = now - _lastAnimated; + + auto interval = now - _lastAnimated; _lastAnimated = now; - - //new global start time code - float nowTime = (float)interval / (float)USECS_PER_SECOND; float oldCurrentFrame = _currentFrame; - _currentFrame = _renderAnimationProperties.getCurrentFrame() + (nowTime * _renderAnimationProperties.getFPS()); - + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); //here we implement the looping animation property //if we have played through the animation once then we hold on the last frame - if( isLooping || (_currentFrame < _endAnim ) ){ + if( isLooping || (_currentFrame < _renderAnimationProperties.getLastFrame() ) ){ //else advance the current frame. //if hold or not playing don't advance the current frame. //also if the animFrame is outside of first or last frame then don't advance the motion. if (!isHolding && entity->getAnimationIsPlaying() && !( _renderAnimationProperties.getCurrentFrame() > _renderAnimationProperties.getLastFrame() ) && !( _renderAnimationProperties.getCurrentFrame() < _renderAnimationProperties.getFirstFrame() ) ) { - //float deltaTime = (float)interval / (float)USECS_PER_SECOND; - //_currentlyPlayingFrame += (deltaTime * _renderAnimationProperties.getFPS()); - //do nothing + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + while ((_currentFrame - _renderAnimationProperties.getFirstFrame()) > updatedFrameCount) { + _currentFrame -= updatedFrameCount; + } } else { //use old currentFrame _currentFrame = oldCurrentFrame; } - } - else { + }else { //make current frame the endanim frame - _currentFrame = _endAnim; + _currentFrame = _renderAnimationProperties.getLastFrame(); } + //save the last place that we rendered ourselves. + //entity->setAnimationCurrentFrame(_currentFrame); { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; + // int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; //this gives us the absolute frame value to use by adding the first frame value. - animationCurrentFrame += firstFrame; - + // animationCurrentFrame += firstFrame; + int animationCurrentFrame = (int)(glm::floor(_currentFrame)); @@ -1383,7 +1383,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { qCDebug(entitiesrenderer) << "this is where the change is currently handled in the rendering code"; - qCDebug(entitiesrenderer) << "getting the currently playing frame from the modelentityitem update" << entity->getCurrentlyPlayingFrame(); + qCDebug(entitiesrenderer) << "getting the currently playing frame from the modelentityitem update" << newAnimationProperties.getCurrentFrame(); withWriteLock([&] { if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { @@ -1392,11 +1392,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _lastAnimated = 0; } }else if ( _renderAnimationProperties.getLoop() && !newAnimationProperties.getLoop()) { - int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); - _endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + //int currentframe_mod_length = (int)(_currentFrame - (int)(glm::floor(newAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(newAnimationProperties.getLastFrame())) - (int)(glm::floor(newAnimationProperties.getFirstFrame())) + 1); + //_endAnim = _currentFrame + ((int)(newAnimationProperties.getLastFrame()) - (int)(newAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; } - _currentlyPlayingFrame = newAnimationProperties.getCurrentlyPlayingFrame(); - qCDebug(entitiesrenderer) << "renderable update to currently playing frame " << _currentlyPlayingFrame; + _currentFrame = newAnimationProperties.getCurrentFrame(); + qCDebug(entitiesrenderer) << "renderable update to current frame " << _currentFrame; _renderAnimationProperties = newAnimationProperties; }); } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index bdf4f6f3f9..b853077ca1 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -199,22 +199,27 @@ void ModelEntityItem::update(const quint64& now) { if (_previousAnimationProperties != currentAnimationProperties) { qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code"; withWriteLock([&] { - if ( (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) || (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + _previousAnimationProperties = currentAnimationProperties; + if ( (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { // if (!(currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getLastFrame()) && !(currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getFirstFrame())) { // _currentlyPlayingFrame = currentAnimationProperties.getCurrentFrame(); //_endAnim = _currentlyPlayingFrame + ( currentAnimationProperties.getLastFrame() - currentAnimationProperties.getFirstFrame() ); //_lastAnimated = 0; // } - setAnimationCurrentlyPlayingFrame(usecTimestampNow()); + qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, current frame is: \n\n" << currentAnimationProperties.getCurrentFrame(); + setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); } - //else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { - // int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); - //_endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; - //} - _previousAnimationProperties = currentAnimationProperties; + else { + //else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { + // int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); + //_endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; + //} + setAnimationCurrentFrame(currentAnimationProperties.getCurrentFrame()); + } + }); - qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, currently playing frame is: " << currentAnimationProperties.getCurrentlyPlayingFrame(); + qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, current frame is: " << currentAnimationProperties.getCurrentFrame(); } //_previousAnimationProperties = currentAnimationProperties; updateFrameCount(); @@ -244,8 +249,8 @@ void ModelEntityItem::updateFrameCount() { //this is now getting the time since the server started the animation. //auto interval = now - _currentlyPlayingFrame; - //auto interval = now - _lastAnimated; - //_lastAnimated = now; + auto interval = now - _lastAnimated; + _lastAnimated = now; @@ -257,20 +262,28 @@ void ModelEntityItem::updateFrameCount() { int firstFrame = getAnimationFirstFrame(); int lastFrame = getAnimationLastFrame(); bool isHolding = getAnimationHold(); + int updatedFrameCount = lastFrame - firstFrame + 1; - //if (isLooping || (_currentFrame < _endAnim)) { + if (isLooping || (_currentFrame < _previousAnimationProperties.getLastFrame())) { //else advance the current frame. //if hold or not playing don't advance the current frame. //also if the animFrame is outside of first or last frame then don't advance the motion. if (!isHolding && getAnimationIsPlaying() && !(_previousAnimationProperties.getCurrentFrame() > _previousAnimationProperties.getLastFrame()) && !(_previousAnimationProperties.getCurrentFrame() < _previousAnimationProperties.getFirstFrame())) { - // float deltaTime = (float)interval / (float)USECS_PER_SECOND; - // _currentlyPlayingFrame += (deltaTime * _previousAnimationProperties.getFPS()); - // qCDebug(entities) << "the frame is now " << _currentlyPlayingFrame; + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * _previousAnimationProperties.getFPS()); + while ((_currentFrame - _previousAnimationProperties.getFirstFrame()) > updatedFrameCount) { + _currentFrame -= updatedFrameCount; + } + qCDebug(entities) << "the frame is now 1 " << _currentFrame; // setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); - setAnimationCurrentlyPlayingFrame(now); + setAnimationCurrentFrame(_currentFrame); } - //} + } else { + _currentFrame = getAnimationLastFrame(); + setAnimationCurrentFrame(_currentFrame); + qCDebug(entities) << "the frame is now 2 " << _currentFrame; + } } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index dad022ae0a..973f49aec4 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -178,6 +178,7 @@ private: AnimationPropertyGroup _previousAnimationProperties; bool _propTestFlag{ true }; bool _propTestFlag2{ true }; + float _currentFrame{ 0 }; //angus }; From 91671722a5fea77be4d59cfede583ab06d891805 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 16:09:54 +1300 Subject: [PATCH 062/137] Add Shape API types JSDoc --- libraries/render-utils/src/GeometryCache.cpp | 26 ++++++++++++++++++++ tools/jsdoc/plugins/hifi.js | 1 + 2 files changed, 27 insertions(+) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index ebf0f13d97..76c354bdf8 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -74,6 +74,32 @@ static std::array MAPPING GeometryCache::Cylinder, } }; +/**jsdoc +*

{@link Entities} and {@link Overlays} may have the following geometrical shapes:

+* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +*
ValueDescription
LineA 1D line oriented in 3 dimensions.
TriangleA triangular prism.
QuadA 2D square oriented in 3 dimensions.
HexagonA hexagonal prism.
OctagonAn octagonal prism.
CircleA 2D circle oriented in 3 dimensions.
CubeA cube.
SphereA sphere.
TetrahedronA tetrahedron.
OctahedronAn octahedron.
DodecahedronA dodecahedron.
IcosahedronAn icosahedron.
TorusA torus. Not implemented.
ConeA cone.
CylinderA cylinder.
+* @typedef {string} Shape +*/ static const std::array GEOCACHE_SHAPE_STRINGS{ { "Line", "Triangle", diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 9818f70f6a..ce691126d8 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -23,6 +23,7 @@ exports.handlers = { '../../libraries/controllers/src/controllers/', '../../libraries/entities/src', '../../libraries/networking/src', + '../../libraries/render-utils/src', '../../libraries/shared/src', '../../libraries/shared/src/shared', '../../libraries/script-engine/src', From 4b5aa9bdec5f639f001b1e37a6c4a03dce827be8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 16:18:14 +1300 Subject: [PATCH 063/137] Add Overlays API functionality JSDoc --- interface/src/ui/overlays/Base3DOverlay.cpp | 35 +- interface/src/ui/overlays/Billboardable.cpp | 5 + interface/src/ui/overlays/Circle3DOverlay.cpp | 89 +++++ interface/src/ui/overlays/Cube3DOverlay.cpp | 70 ++-- interface/src/ui/overlays/Grid3DOverlay.cpp | 52 +++ interface/src/ui/overlays/Image3DOverlay.cpp | 52 +++ interface/src/ui/overlays/ImageOverlay.cpp | 23 ++ interface/src/ui/overlays/Line3DOverlay.cpp | 58 ++++ interface/src/ui/overlays/ModelOverlay.cpp | 72 ++++- interface/src/ui/overlays/Overlay.cpp | 28 +- interface/src/ui/overlays/Overlay2D.cpp | 9 + interface/src/ui/overlays/Overlays.cpp | 27 +- interface/src/ui/overlays/Overlays.h | 305 +++++++++++++----- interface/src/ui/overlays/PanelAttachable.cpp | 2 + interface/src/ui/overlays/Planar3DOverlay.cpp | 4 + interface/src/ui/overlays/QmlOverlay.cpp | 1 + .../src/ui/overlays/Rectangle3DOverlay.cpp | 45 +++ .../src/ui/overlays/RectangleOverlay.cpp | 23 ++ interface/src/ui/overlays/Shape3DOverlay.cpp | 48 +++ interface/src/ui/overlays/Sphere3DOverlay.cpp | 76 +++-- interface/src/ui/overlays/Text3DOverlay.cpp | 59 ++++ interface/src/ui/overlays/TextOverlay.cpp | 27 ++ interface/src/ui/overlays/Web3DOverlay.cpp | 58 ++++ 23 files changed, 995 insertions(+), 173 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 6e921bbc81..24ba049c56 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -202,23 +202,28 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { // JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. /**jsdoc - * @typedef - * @property {string} name - TODO + * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start - * @property {Vec3} localPosition - The local position of the overlay relative to its parent. + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent. - * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, - * filled, or filed. Deprecated: The erroneous property spelling - * "filed" is deprecated and support for it will be removed shortly. - * @property {boolean} isWire - TODO. Synonym: wire. TODO: Actuall, this is an antonym?! - * @property {boolean} isDashedLine - TODO. Synonym: dashed. - * @property {boolean} ignoreRayIntersection - TODO. - * @property {boolean} drawInFront - TODO. - * @property {boolean} grabbable - TODO. - * @property {Uuid} parentID - TODO. - * @property {number} parentJointIndex - TODO. Integer. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". */ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "name") { diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp index 5e912864fc..af076a310a 100644 --- a/interface/src/ui/overlays/Billboardable.cpp +++ b/interface/src/ui/overlays/Billboardable.cpp @@ -23,6 +23,11 @@ void Billboardable::setProperties(const QVariantMap& properties) { } } +// JSDoc for copying to @typedefs of overlay types that inherit Billboardable. +/**jsdoc + * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + */ QVariant Billboardable::getProperty(const QString &property) { if (property == "isFacingAvatar") { return isFacingAvatar(); diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 69ce331c99..74133147c6 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -368,6 +368,95 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { } } +// Overlay's color and alpha properties are overridden. +/**jsdoc + * These are the properties of a circle3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Circle3DProperties + * + * @property {string} type=circle3d - Has the value "circle3d". Read-only. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + * Not used. + * + * @property {number} startAt=0 - The counter-clockwise angle from the overlay's x-axis that drawing starts at, in degrees. + * @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees. + * @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: radius. + * @property {number} innerRadius=0 - The inner radius of the overlay, in meters. + * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of + * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. + * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. + * Write-only. + * @property {Color} endColor - Sets the values of innerEndColor and outerEndColor. + * Write-only. + * @property {Color} innerColor - Sets the values of innerStartColor and innerEndColor. + * Write-only. + * @property {Color} outerColor - Sets the values of outerStartColor and outerEndColor. + * Write-only. + * @property {Color} innerStartcolor - The color at the inner start point of the overlay. Write-only. + * @property {Color} innerEndColor - The color at the inner end point of the overlay. Write-only. + * @property {Color} outerStartColor - The color at the outer start point of the overlay. Write-only. + * @property {Color} outerEndColor - The color at the outer end point of the overlay. Write-only. + * @property {number} alpha=0.5 - The opacity of the overlay, 0.0 - 1.0. Setting this value also sets + * the values of innerStartAlpha, innerEndAlpha, outerStartAlpha, and + * outerEndAlpha. Synonym: Alpha; write-only. + * @property {number} startAlpha - Sets the values of innerStartAlpha and outerStartAlpha. + * Write-only. + * @property {number} endAlpha - Sets the values of innerEndAlpha and outerEndAlpha. + * Write-only. + * @property {number} innerAlpha - Sets the values of innerStartAlpha and innerEndAlpha. + * Write-only. + * @property {number} outerAlpha - Sets the values of outerStartAlpha and outerEndAlpha. + * Write-only. + * @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay. Write-only. + * @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay. Write-only. + * @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. Write-only. + * @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. Write-only. + + * @property {boolean} hasTickMarks=false - If true then tick marks are drawn. + * @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees. + * @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees. + * @property {number} majorTickMarksLength=0 - The length of the major tick marks, in meters. A positive value draws tick marks + * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {number} minorTickMarksLength=0 - The length of the minor tick arks, in meters. A positive value draws tick marks + * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {Color} majorTickMarksColor=0,0,0 - The color of the major tick marks. + * @property {Color} minorTickMarksColor=0,0,0 - The color of the minor tick marks. + */ QVariant Circle3DOverlay::getProperty(const QString& property) { if (property == "startAt") { return _startAt; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 57b692f652..1923e085bd 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -135,37 +135,51 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { } /**jsdoc - * @typedef {object} Overlays.cube3dProperties - * @property {number} borderSize - TODO + * These are the properties of a cube {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.CubeProperties * + * @property {string} type=cube - Has the value "cube". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. * - * @property {string} name - TODO - * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and - * start - * @property {Vec3} localPosition - The local position of the overlay relative to its parent. - * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. - * @property {Quat} localRotation - The orientation of the overlay relative to its parent. - * @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, and - * filled. - * @property {boolean} isWire - TODO. Synonym: wire. - * @property {boolean} isDashedLine - TODO. Synonym: dashed. - * @property {boolean} ignoreRayIntersection - TODO. - * @property {boolean} drawInFront - TODO. - * @property {boolean} grabbable - TODO. - * @property {Uuid} parentID - TODO. - * @property {number} parentJointIndex - TODO. Integer. - * - * @property {string} type - TODO. - * @property {RGB} color - TODO. - * @property {number} alpha - TODO. - * @property {number} pulseMax - TODO. - * @property {number} pulseMin - TODO. - * @property {number} pulsePeriod - TODO. - * @property {number} alphaPulse - TODO. - * @property {number} colorPulse - TODO. - * @property {boolean} visible - TODO. - * @property {string} anchor - TODO. + * @property {number} borderSize - Not used. */ QVariant Cube3DOverlay::getProperty(const QString& property) { if (property == "borderSize") { diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 6fd132b531..1d36e3a128 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -112,6 +112,58 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) { updateGrid(); } +/**jsdoc + * These are the properties of a grid {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.GridProperties + * + * @property {string} type=grid - Has the value "grid". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + * + * @property {boolean} followCamera=true - If true, the grid is always visible even as the camera moves to another + * position. + * @property {number} majorGridEvery=5 - Integer number of minorGridEvery intervals at which to draw a thick grid + * line. Minimum value = 1. + * @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value = + * 0.001. + */ QVariant Grid3DOverlay::getProperty(const QString& property) { if (property == "followCamera") { return _followCamera; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index a8ae293c83..9f5c3e8116 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -188,6 +188,58 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { } } +/**jsdoc + * These are the properties of a image3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Image3DProperties + * + * @property {string} type=image3d - Has the value "image3d". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + * + * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * + * @property {string} url - The URL of the PNG or JPG image to display. + * @property {Rect} subImage - The portion of the image to display. Defaults to the full image. + * @property {boolean} emissive - If true then the overlay is displayed at full brightness. + */ QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "url") { return _url; diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 4077fb0ab5..0faa27b866 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -19,6 +19,29 @@ QString const ImageOverlay::TYPE = "image"; QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml")); +// ImageOverlay's properties are defined in the QML file specified above. +/**jsdoc + * These are the properties of an image {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.ImageProperties + * + * @property {Rect} bounds - The position and size of the image display area, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value of the image display area = bounds.x. + * Write-only. + * @property {number} y - Integer top, y-coordinate value of the image display area = bounds.y. + * Write-only. + * @property {number} width - Integer width of the image display area = bounds.width. Write-only. + * @property {number} height - Integer height of the image display area = bounds.height. Write-only. + * @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the bounds. + * Write-only. + * @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from. + * Write-only. + * @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. Write-only. + * @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, 0.0 - + * 1.0. Write-only. + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * Write-only. + */ + ImageOverlay::ImageOverlay() : QmlOverlay(URL) { } diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d59e382613..31a98d1078 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -245,6 +245,64 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { } } +/**jsdoc + * These are the properties of a line3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Line3DProperties + * + * @property {string} type=line3d - Has the value "line3d". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to. + * @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is + * attached to if parentID is an avatar skeleton. A value of 65535 means "no joint". + * @property {Vec3} start - The start point of the line. Synonyms: startPoint, p1, and + * position. + * @property {Vec3} end - The end point of the line. Synonyms: endPoint and p2. + * @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as start. Synonym: localPosition. + * @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a + * endParentID set, otherwise the same value as end. + * @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end + * points. + * @property {number} glow=0 - If glow > 0, the line is rendered with a glow. + * @property {number} lineWidth=0.02 - If glow > 0, this is the width of the glow, in meters. + */ QVariant Line3DOverlay::getProperty(const QString& property) { if (property == "start" || property == "startPoint" || property == "p1") { return vec3toVariant(getStart()); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 2a13249958..90e16599e2 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -138,7 +138,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { _scaleToFit = true; setDimensions(vec3FromVariant(dimensions)); } else if (scale.isValid()) { - // if "scale" property is set but "dimentions" is not. + // if "scale" property is set but "dimensions" is not. // do NOT scale to fit. _scaleToFit = false; } @@ -167,6 +167,10 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { Q_ARG(const QVariantMap&, textureMap)); } + // jointNames is read-only. + // jointPositions is read-only. + // jointOrientations is read-only. + // relative auto jointTranslationsValue = properties["jointTranslations"]; if (jointTranslationsValue.canConvert(QVariant::List)) { @@ -262,6 +266,72 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { return result; } +// Note: ModelOverlay overrides Volume3DOverlay's "dimensions" and "scale" properties. +/**jsdoc + * These are the properties of a model {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.ModelProperties + * + * @property {string} type=sphere - Has the value "model". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {string} url - The URL of the FBX or OBJ model used for the overlay. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonym: size. + * @property {Vec3} scale - The scale factor applied to the model's dimensions. + * @property {object.} textures - Maps the named textures in the model to the JPG or PNG images in the urls. + * @property {Array.} jointNames - The names of the joints - if any - in the model. Read-only + * @property {Array.} jointRotations - The relative rotations of the model's joints. + * @property {Array.} jointTranslations - The relative translations of the model's joints. + * @property {Array.} jointOrientations - The absolute orientations of the model's joints, in world coordinates. + * Read-only + * @property {Array.} jointPositions - The absolute positions of the model's joints, in world coordinates. + * Read-only + * @property {string} animationSettings.url="" - The URL of an FBX file containing an animation to play. + * @property {number} animationSettings.fps=0 - The frame rate (frames/sec) to play the animation at. + * @property {number} animationSettings.firstFrame=0 - The frame to start playing at. + * @property {number} animationSettings.lastFrame=0 - The frame to finish playing at. + * @property {boolean} animationSettings.running=false - Whether or not the animation is playing. + * @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop. + * @property {boolean} animationSettings.hold=false - Whether or not the when the animation finishes, the rotations and + * translations of the last frame played should be maintained. + * @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should + * be played. + */ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "url") { return _url.toString(); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 0a4b16c899..b17a462ebb 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -103,18 +103,22 @@ void Overlay::setProperties(const QVariantMap& properties) { // JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. /**jsdoc - * @typedef - * @property {string} type - TODO. - * @property {RGB} color - TODO. - * @property {number} alpha - TODO. - * @property {number} pulseMax - TODO. - * @property {number} pulseMin - TODO. - * @property {number} pulsePeriod - TODO. - * @property {number} alphaPulse - TODO. - * @property {number} colorPulse - TODO. - * @property {boolean} visible - TODO. - * @property {string} anchor - TODO. - */ + * @property {string} type=TODO - Has the value "TODO". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + */ QVariant Overlay::getProperty(const QString& property) { if (property == "type") { return QVariant(getType()); diff --git a/interface/src/ui/overlays/Overlay2D.cpp b/interface/src/ui/overlays/Overlay2D.cpp index d5f88a84d7..71b74e9452 100644 --- a/interface/src/ui/overlays/Overlay2D.cpp +++ b/interface/src/ui/overlays/Overlay2D.cpp @@ -23,6 +23,15 @@ AABox Overlay2D::getBounds() const { glm::vec3(_bounds.width(), _bounds.height(), 0.01f)); } +// JSDoc for copying to @typedefs of overlay types that inherit Overlay2D. +// QmlOverlay-derived classes don't support getProperty(). +/**jsdoc + * @property {Rect} bounds - The position and size of the rectangle. Write-only. + * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. + * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. + * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. + * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. + */ void Overlay2D::setProperties(const QVariantMap& properties) { Overlay::setProperties(properties); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b4de2ff7af..0363bdb75e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -161,19 +161,18 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) Overlay::Pointer thisOverlay = nullptr; /**jsdoc - *

Overlays may be one of the following types:

+ *

An overlay may be one of the following types:

* * * * * - * * * * - * + * * - * + * * * * @@ -184,6 +183,7 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) * * *
Value2D/3DDescription
billboard3DSynonym for image3d.
circle3d3DA circle.
cube3DA cube. Can also use a shape to create a cube.
grid3DA grid of lines in a plane.
image2DAn image.
image2DAn image. Synonym: billboard.
image3d3DAn image.
line3d3DA line
line3d3DA line.
model3DA model.
rectangle2DA rectangle.
rectangle3d3DA rectangle.
web3d3DWeb content.
+ *

Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.

* @typedef {string} Overlays.OverlayType */ @@ -191,12 +191,23 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) *

Different overlay types have different properties:

* * - * + * * * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * + * + * * *
Overlay TypeOverlay Properties
{@link Overlays.OverlayType|OverlayType}Overlay Properties
cube3d{@link Overlays.cube3dProperties|cube3dProperties}
sphere3d{@link Overlays.sphere3dProperties|sphere3dProperties}
TODOTODO
circle3d{@link Overlays.Circle3DProperties|Circle3DProperties}
cube{@link Overlays.CubeProperties|CubeProperties}
grid{@link Overlays.GridProperties|GridProperties}
image{@link Overlays.ImageProperties|ImageProperties}
image3d{@link Overlays.Image3DProperties|Image3DProperties}
line3d{@link Overlays.Line3DProperties|Line3DProperties}
model{@link Overlays.ModelProperties|ModelProperties}
rectangle{@link Overlays.RectangleProperties|RectangleProperties}
rectangle3d{@link Overlays.Rectangle3DProperties|Rectangle3DProperties}
shape{@link Overlays.ShapeProperties|ShapeProperties}
sphere{@link Overlays.SphereProperties|SphereProperties}
text{@link Overlays.TextProperties|TextProperties}
text3d{@link Overlays.Text3DProperties|Text3DProperties}
web3d{@link Overlays.Web3DProperties|Web3DProperties}
* @typedef {object} Overlays.OverlayProperties diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 4554914bb4..6561a99d0a 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -47,11 +47,11 @@ const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); /**jsdoc * @typedef {object} Overlays.RayToOverlayIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise - * false. TODO: Only 3D overlay, really? What about the other properties? - * @property {Uuid} overlayID - The UUID of the overlay that was intersected with. + * false. + * @property {Uuid} overlayID - The UUID of the overlay that was intersected. * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. - * @property {Vec3} surfaceNormal - The normal of the surface that was intersected with. - * @property {Vec3} intersection - The point at which the {@link PickRay} intersected with the overlay. + * @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point. + * @property {Vec3} intersection - The position of the intersection point. */ class RayToOverlayIntersectionResult { public: @@ -72,9 +72,10 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R /**jsdoc * The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to - * yourself and that aren't persisted to the domain. They are primarily used for UI. + * yourself and that aren't persisted to the domain. They are used for UI. * @namespace Overlays * @property {Uuid} keyboardFocusOverlay - Get or set the [web3d]{@link Overlays.OverlayType} overlay that has keyboard focus. + * If no overlay is set, get returns null; set null to clear keyboard focus. */ class Overlays : public QObject { @@ -118,94 +119,114 @@ public slots: * @function Overlays.addOverlay * @param {Overlays.OverlayType} type - The type of the overlay to add. * @param {Overlays.OverlayProperties} properties - The properties of the overlay to add. - * @return {Uuid} The UUID of the newly created overlay. + * @returns {Uuid} The ID of the newly created overlay. */ OverlayID addOverlay(const QString& type, const QVariant& properties); /**jsdoc * Create a clone of an existing overlay. - * * @function Overlays.cloneOverlay - * @param {Overlays.OverlayID} overlayID The ID of the overlay to clone. - * @return {Overlays.OverlayID} The ID of the new overlay. + * @param {Uuid} overlayID - The ID of the overlay to clone. + * @returns {Uuid} The ID of the new overlay. */ OverlayID cloneOverlay(OverlayID id); /**jsdoc * Edit an overlay's properties. - * * @function Overlays.editOverlay - * @param {Overlays.OverlayID} overlayID The ID of the overlay to edit. - * @return {bool} `true` if the overlay was found and edited, otherwise false. + * @param {Uuid} overlayID - The ID of the overlay to edit. + * @param {Overlays.OverlayProperties} properties - The properties changes to make. + * @returns {boolean} true if the overlay was found and edited, otherwise false. */ bool editOverlay(OverlayID id, const QVariant& properties); - /// edits an overlay updating only the included properties, will return the identified OverlayID in case of - /// successful edit, if the input id is for an unknown overlay this function will have no effect + /**jsdoc + * Edit multiple overlays' properties. + * @function Overlays.editOverlays + * @param propertiesById {object.} - On object with overlay IDs as keys and + * {@link Overlays.OverlayProperties|OverlayProperties} edits to make for each as values. + * @returns {boolean} true if all overlays were found and edited, otherwise false. + * @example Demonstrate creating the propertiesById parameter. + * var overlayEdits = {}; + * overlayEdits[overlayA] = propertiesA; + * overlayEdits[overlayB] = propertiesB; + * var success = Overlays.editOverlays(overlayEdits); + */ bool editOverlays(const QVariant& propertiesById); /**jsdoc * Delete an overlay. - * * @function Overlays.deleteOverlay - * @param {Overlays.OverlayID} overlayID The ID of the overlay to delete. + * @param {Uuid} overlayID - The ID of the overlay to delete. */ void deleteOverlay(OverlayID id); /**jsdoc * Get the type of an overlay. - * * @function Overlays.getOverlayType - * @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of. - * @return {string} The type of the overlay if found, otherwise the empty string. + * @param {Uuid} overlayID - The ID of the overlay to get the type of. + * @returns {Overlays.OverlayType} The type of the overlay if found, otherwise an empty string. */ QString getOverlayType(OverlayID overlayId); /**jsdoc - * Get the overlay Script object. - * - * @function Overlays.getOverlayObject - * @param {Overlays.OverlayID} overlayID The ID of the overlay to get the script object of. - * @return {Object} The script object for the overlay if found. - */ + * Get the overlay script object. + * @function Overlays.getOverlayObject + * @param {Uuid} overlayID - The ID of the overlay to get the script object of. + * @returns {object} The script object for the overlay if found. + */ QObject* getOverlayObject(OverlayID id); /**jsdoc - * Get the ID of the overlay at a particular point on the HUD/screen. - * + * Get the ID of the overlay at a particular point on the screen or HUD. * @function Overlays.getOverlayAtPoint - * @param {Vec2} point The point to check for an overlay. - * @return {Overlays.OverlayID} The ID of the overlay at the point specified. - * If no overlay is found, `0` will be returned. + * @param {Vec2} point - The point to check for an overlay. + * @returns {Uuid} The ID of the overlay at the specified point if found, otherwise null. */ OverlayID getOverlayAtPoint(const glm::vec2& point); /**jsdoc - * Get the value of a an overlay's property. - * + * Get the value of an overlay's property. * @function Overlays.getProperty - * @param {Overlays.OverlayID} The ID of the overlay to get the property of. - * @param {string} The name of the property to get the value of. - * @return {Object} The value of the property. If the overlay or the property could - * not be found, `undefined` will be returned. + * @param {Uuid} overlayID - The ID of the overlay. + * @param {string} property - The name of the property value to get. + * @returns {object} The value of the property if the overlay and property can be found, otherwise undefined. */ OverlayPropertyResult getProperty(OverlayID id, const QString& property); + /**jsdoc + * Get the values of an overlay's properties. + * @function Overlays.getProperties + * @param {Uuid} overlayID - The ID of the overlay. + * @param {Array.} properties - An array of names of properties to get the values of. + * @returns {Overlays.OverlayProperties} The values of valid properties if the overlay can be found, otherwise + * undefined. + */ OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties); + /**jsdoc + * Get the values of multiple overlays' properties. + * @function Overlays.getOverlaysProperties + * @param propertiesById {object.>} - An object with overlay IDs as keys and arrays of the names of + * properties to get for each as values. + * @returns {object.} An object with overlay IDs as keys and + * {@link Overlays.OverlayProperties|OverlayProperties} as values. + */ OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties); - /*jsdoc - * Find the closest 3D overlay hit by a pick ray. - * + /**jsdoc + * Find the closest 3D overlay intersected by a {@link PickRay}. * @function Overlays.findRayIntersection - * @param {PickRay} The PickRay to use for finding overlays. - * @param {bool} Unused; Exists to match Entity interface - * @param {List of Overlays.OverlayID} Whitelist for intersection test. - * @param {List of Overlays.OverlayID} Blacklist for intersection test. - * @param {bool} Unused; Exists to match Entity interface - * @param {bool} Unused; Exists to match Entity interface - * @return {Overlays.RayToOverlayIntersectionResult} The result of the ray cast. + * @param {PickRay} pickRay - The PickRay to use for finding overlays. + * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. + * @param {Array.} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited + * to overlays in the list. + * @param {Array.} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't + * exclude overlays in the list. + * @param {boolean} [visibleOnly=false] - Unused; exists to match Entity API. + * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. + * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking + * into account overlayIDsToInclude and overlayIDsToExclude if they're not empty. */ RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, @@ -214,61 +235,77 @@ public slots: bool visibleOnly = false, bool collidableOnly = false); - // Same as above but with QVectors + // TODO: Apart from the name, this function signature on JavaScript is identical to that of findRayIntersection() and should + // probably be removed from the JavaScript API so as not to cause confusion. + /**jsdoc + * Find the closest 3D overlay intersected by a {@link PickRay}. + * @function Overlays.findRayIntersectionVector + * @deprecated Use {@link Overlays.findRayIntersection} instead; it has identical parameters and results. + * @param {PickRay} pickRay - The PickRay to use for finding overlays. + * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. + * @param {Array.} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited + * to overlays in the list. + * @param {Array.} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't + * exclude overlays in the list. + * @param {boolean} [visibleOnly=false] - Unused; exists to match Entity API. + * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. + * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking + * into account overlayIDsToInclude and overlayIDsToExclude if they're not empty. + */ RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, const QVector& overlaysToInclude, const QVector& overlaysToDiscard, bool visibleOnly = false, bool collidableOnly = false); /**jsdoc - * Return a list of 3d overlays with bounding boxes that touch the given sphere - * + * Return a list of 3D overlays with bounding boxes that touch a search sphere. * @function Overlays.findOverlays - * @param {Vec3} center the point to search from. - * @param {float} radius search radius - * @return {Overlays.OverlayID[]} list of overlays withing the radius + * @param {Vec3} center - The center of the search sphere. + * @param {number} radius - The radius of the search sphere. + * @returns {Uuid[]} An array of overlay IDs with bounding boxes that touch a search sphere. */ QVector findOverlays(const glm::vec3& center, float radius); /**jsdoc - * Check whether an overlay's assets have been loaded. For example, if the - * overlay is an "image" overlay, this will indicate whether the its image - * has loaded. + * Check whether an overlay's assets have been loaded. For example, for an image overlay the result indicates + * whether its image has been loaded. * @function Overlays.isLoaded - * @param {Overlays.OverlayID} The ID of the overlay to check. - * @return {bool} `true` if the overlay's assets have been loaded, otherwise `false`. + * @param {Uuid} overlayID - The ID of the overlay to check. + * @returns {boolean} true if the overlay's assets have been loaded, otherwise false. */ bool isLoaded(OverlayID id); /**jsdoc * Calculates the size of the given text in the specified overlay if it is a text overlay. - * If it is a 2D text overlay, the size will be in pixels. - * If it is a 3D text overlay, the size will be in meters. - * * @function Overlays.textSize - * @param {Overlays.OverlayID} The ID of the overlay to measure. - * @param {string} The string to measure. - * @return {Vec2} The size of the text. + * @param {Uuid} overlayID - The ID of the overlay to use for calculation. + * @param {string} text - The string to calculate the size of. + * @returns {Vec2} The size of the text if the overlay is a text overlay, otherwise + * { x: 0, y: 0 }. If the overlay is a 2D overlay the size is in pixels; if the overlay if a 3D overlay the + * size is in meters. */ QSizeF textSize(OverlayID id, const QString& text); /**jsdoc - * Get the width of the virtual 2D HUD. - * + * Get the width of the window or HUD. * @function Overlays.width - * @return {float} The width of the 2D HUD. + * @returns {number} The width, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode. */ float width(); /**jsdoc - * Get the height of the virtual 2D HUD. - * + * Get the height of the window or HUD. * @function Overlays.height - * @return {float} The height of the 2D HUD. + * @returns {number} The height, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode. */ float height(); - /// return true if there is an overlay with that id else false + /**jsdoc + * Check if there is an overlay of a given ID. + * @function Overlays.isAddedOverly + * @param {Uuid} overlayID - The ID to check. + * @returns {boolean} true if an overlay with the given ID exists, false otherwise. + */ bool isAddedOverlay(OverlayID id); #if OVERLAY_PANELS @@ -295,12 +332,52 @@ public slots: #endif + /**jsdoc + * Generate a mouse press event on an overlay. + * @function Overlays.sendMousePressOnOverlay + * @param {Uuid} overlayID - The ID of the overlay to generate a mouse press event on. + * @param {PointerEvent} event - The mouse press event details. + */ void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + + /**jsdoc + * Generate a mouse release event on an overlay. + * @function Overlays.sendMouseReleaseOnOverlay + * @param {Uuid} overlayID - The ID of the overlay to generate a mouse release event on. + * @param {PointerEvent} event - The mouse release event details. + */ void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + + /**jsdoc + * Generate a mouse move event on an overlay. + * @function Overlays.sendMouseMoveOnOverlay + * @param {Uuid} overlayID - The ID of the overlay to generate a mouse move event on. + * @param {PointerEvent} event - The mouse moved event details. + */ void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + /**jsdoc + * Generate a hover enter event on an overlay. + * @function Overlays.sendHoverEnterOverlay + * @param {Uuid} id - The ID of the overlay to generate a hover enter event on. + * @param {PointerEvent} event - The hover enter event details. + */ void sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); + + /**jsdoc + * Generate a hover over event on an overlay. + * @function Overlays.sendHoverOverOverlay + * @param {Uuid} overlayID - The ID of the overlay to generate a hover over event on. + * @param {PointerEvent} event - The hover over event details. + */ void sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event); + + /**jsdoc + * Generate a hover leave event on an overlay. + * @function Overlays.sendHoverLeaveOverlay + * @param {Uuid} overlayID - The ID of the overlay to generate a hover leave event on. + * @param {PointerEvent} event - The hover leave event details. + */ void sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); /**jsdoc @@ -312,19 +389,19 @@ public slots: OverlayID getKeyboardFocusOverlay(); /**jsdoc - * Set the ID of the overlay that has keyboard focus. - * @function Overlays.setKeyboardFocusOverlay - * @param {Uuid} id - The ID of the [web3d]{@link Overlays.OverlayType} overlay to set keyboard focus to. Use - * {@link Uuid|Uuid.NULL} or null to unset keyboard focus from an overlay. - */ + * Set the Web3D overlay that has keyboard focus. + * @function Overlays.setKeyboardFocusOverlay + * @param {Uuid} overlayID - The ID of the [web3d]{@link Overlays.OverlayType} overlay to set keyboard focus to. Use + * {@link Uuid|Uuid.NULL} or null to unset keyboard focus from an overlay. + */ void setKeyboardFocusOverlay(const OverlayID& id); signals: /**jsdoc - * Emitted when an overlay is deleted - * + * Triggered when an overlay is deleted. * @function Overlays.overlayDeleted - * @param {OverlayID} The ID of the overlay that was deleted. + * @param {Uuid} overlayID - The ID of the overlay that was deleted. + * @returns {Signal} */ void overlayDeleted(OverlayID id); @@ -332,15 +409,87 @@ signals: void panelDeleted(OverlayID id); #endif + /**jsdoc + * Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay). + * @function Overlays.mousePressOnOverlay + * @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on. + * @param {PointerEvent} event - The mouse press event details. + * @returns {Signal} + */ void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse double press event occurs on an overlay. Only occurs for 3D overlays. + * @function Overlays.mouseDoublePressOnOverlay + * @param {Uuid} overlayID - The ID of the overlay the mouse double press event occurred on. + * @param {PointerEvent} event - The mouse double press event details. + * @returns {Signal} + */ void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse release event occurs on an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendMouseReleaseOnOverlay|sendMouseReleaseOnOverlay} for a 2D overlay). + * @function Overlays.mouseReleaseOnOverlay + * @param {Uuid} overlayID - The ID of the overlay the mouse release event occurred on. + * @param {PointerEvent} event - The mouse release event details. + * @returns {Signal} + */ void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse move event occurs on an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendMouseMoveOnOverlay|sendMouseMoveOnOverlay} for a 2D overlay). + * @function Overlays.mouseMoveOnOverlay + * @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on. + * @param {PointerEvent} event - The mouse move event details. + * @returns {Signal} + */ void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse press event occurs on something other than an overlay. Only occurs for 3D overlays. + * @function Overlays.mousePressOffOverlay + * @returns {Signal} + */ void mousePressOffOverlay(); + + /**jsdoc + * Triggered when a mouse double press event occurs on something other than an overlay. Only occurs for 3D overlays. + * @function Overlays.mouseDoublePressOffOverlay + * @returns {Signal} + */ void mouseDoublePressOffOverlay(); + /**jsdoc + * Triggered when a mouse cursor starts hovering over an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendHoverEnterOverlay|sendHoverEnterOverlay} for a 2D overlay). + * @function Overlays.hoverEnterOverlay + * @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on. + * @param {PointerEvent} event - The mouse move event details. + * @returns {Signal} + */ void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse cursor continues hovering over an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendHoverOverOverlay|sendHoverOverOverlay} for a 2D overlay). + * @function Overlays.hoverOverOverlay + * @param {Uuid} overlayID - The ID of the overlay the hover over event occurred on. + * @param {PointerEvent} event - The hover over event details. + * @returns {Signal} + */ void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse cursor finishes hovering over an overlay. Only occurs for 3D overlays (unless you use + * {@link Overlays.sendHoverLeaveOverlay|sendHoverLeaveOverlay} for a 2D overlay). + * @function Overlays.hoverLeaveOverlay + * @param {Uuid} overlayID - The ID of the overlay the hover leave event occurred on. + * @param {PointerEvent} event - The hover leave event details. + * @returns {Signal} + */ void hoverLeaveOverlay(OverlayID overlayID, const PointerEvent& event); private: diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index aae8893667..bcd32b2850 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -27,6 +27,8 @@ bool PanelAttachable::getParentVisible() const { #endif } +// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable. +// No JSDoc because these properties are not actually used. QVariant PanelAttachable::getProperty(const QString& property) { if (property == "offsetPosition") { return vec3toVariant(getOffsetPosition()); diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index a9f0bfe2f1..a2955e1c2c 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -58,6 +58,10 @@ void Planar3DOverlay::setProperties(const QVariantMap& properties) { } } +// JSDoc for copying to @typedefs of overlay types that inherit Planar3DOverlay. +/**jsdoc + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + */ QVariant Planar3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { return vec2toVariant(getDimensions()); diff --git a/interface/src/ui/overlays/QmlOverlay.cpp b/interface/src/ui/overlays/QmlOverlay.cpp index 15e72cf1e3..f4a9034187 100644 --- a/interface/src/ui/overlays/QmlOverlay.cpp +++ b/interface/src/ui/overlays/QmlOverlay.cpp @@ -53,6 +53,7 @@ QmlOverlay::~QmlOverlay() { _qmlElement.reset(); } +// QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties. void QmlOverlay::setProperties(const QVariantMap& properties) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties)); diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index f2e324206d..6be5cf0936 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -107,6 +107,51 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() { return builder.build(); } +/**jsdoc + * These are the properties of a rectangle3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Rectangle3DProperties + * + * @property {string} type=rectangle3d - Has the value "rectangle3d". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + */ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) { Planar3DOverlay::setProperties(properties); } diff --git a/interface/src/ui/overlays/RectangleOverlay.cpp b/interface/src/ui/overlays/RectangleOverlay.cpp index 1487a4cb63..0b19297464 100644 --- a/interface/src/ui/overlays/RectangleOverlay.cpp +++ b/interface/src/ui/overlays/RectangleOverlay.cpp @@ -11,6 +11,29 @@ QString const RectangleOverlay::TYPE = "rectangle"; QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml")); +// RectangleOverlay's properties are defined in the QML file specified above. +/**jsdoc + * These are the properties of a rectangle {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.RectangleProperties + * + * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. + * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. + * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. + * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. + * + * @property {Color} color=0,0,0 - The color of the overlay. Write-only. + * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. + * @property {number} borderWidth=1 - Integer width of the border, in pixels. The border is drawn within the rectangle's bounds. + * It is not drawn unless either borderColor or borderAlpha are specified. Write-only. + * @property {number} radius=0 - Integer corner radius, in pixels. Write-only. + * @property {Color} borderColor=0,0,0 - The color of the border. Write-only. + * @property {number} borderAlpha=1.0 - The opacity of the border, 0.0 - 1.0. + * Write-only. + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * Write-only. + */ + RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {} RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay) diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index bd8e6a9728..4bcfd419c1 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -107,6 +107,54 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { } } +/**jsdoc + * These are the properties of a shape {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.ShapeProperties + * + * @property {string} type=shape - Has the value "shape". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * + * @property {Shape} shape=Hexagon - The geometrical shape of the overlay. + * @property {number} borderSize - Not used. + */ QVariant Shape3DOverlay::getProperty(const QString& property) { if (property == "borderSize") { return _borderSize; diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 2403dc16f0..5fe5ff0541 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -26,38 +26,52 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : { } +// If Sphere3DOverlay had a getProperty() method then it would go here; do JSDoc here. /**jsdoc -* @typedef {object} Overlays.sphere3dProperties -* -* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. -* -* @property {string} name - TODO -* @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and -* start -* @property {Vec3} localPosition - The local position of the overlay relative to its parent. -* @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. -* @property {Quat} localRotation - The orientation of the overlay relative to its parent. -* @property {boolean} isSolid - TODO w.r.t. isWire and isDashedLine. Synonyms: solid, isFilled, and -* filled. -* @property {boolean} isWire - TODO. Synonym: wire. -* @property {boolean} isDashedLine - TODO. Synonym: dashed. -* @property {boolean} ignoreRayIntersection - TODO. -* @property {boolean} drawInFront - TODO. -* @property {boolean} grabbable - TODO. -* @property {Uuid} parentID - TODO. -* @property {number} parentJointIndex - TODO. Integer. -* -* @property {string} type - TODO. -* @property {RGB} color - TODO. -* @property {number} alpha - TODO. -* @property {number} pulseMax - TODO. -* @property {number} pulseMin - TODO. -* @property {number} pulsePeriod - TODO. -* @property {number} alphaPulse - TODO. -* @property {number} colorPulse - TODO. -* @property {boolean} visible - TODO. -* @property {string} anchor - TODO. -*/ + * These are the properties of a sphere {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.SphereProperties + * + * @property {string} type=sphere - Has the value "sphere". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + */ void Sphere3DOverlay::render(RenderArgs* args) { if (!_renderVisible) { diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 1cbc73304b..2dfbf22b9d 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -204,6 +204,65 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { } } +/**jsdoc + * These are the properties of a text3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Text3DProperties + * + * @property {string} type=text3d - Has the value "text3d". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. + * + * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * + * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. + * @property {number} textAlpha=1 - The text alpha value. + * @property {Color} backgroundColor=0,0,0 - The background color. + * @property {number} backgroundAlpha=0.7 - The background alpha value. + * @property {number} lineHeight=1 - The height of a line of text in meters. + * @property {number} leftMargin=0.1 - The left margin, in meters. + * @property {number} topMargin=0.1 - The top margin, in meters. + * @property {number} rightMargin=0.1 - The right margin, in meters. + * @property {number} bottomMargin=0.1 - The bottom margin, in meters. + */ + QVariant Text3DOverlay::getProperty(const QString& property) { if (property == "text") { return getText(); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 762d855a11..e937481823 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -27,6 +27,33 @@ QString const TextOverlay::TYPE = "text"; QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml")); +// TextOverlay's properties are defined in the QML file specified above. +/**jsdoc + * These are the properties of a text {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.TextProperties + * + * @property {Rect} bounds - The position and size of the rectangle, in pixels. Write-only. + * @property {number} x - Integer left, x-coordinate value = bounds.x. Write-only. + * @property {number} y - Integer top, y-coordinate value = bounds.y. Write-only. + * @property {number} width - Integer width of the rectangle = bounds.width. Write-only. + * @property {number} height - Integer height of the rectangle = bounds.height. Write-only. + * + * @property {number} margin=0 - Sets the leftMargin and topMargin values, in pixels. + * Write-only. + * @property {number} leftMargin=0 - The left margin's size, in pixels. Write-only. + * @property {number} topMargin=0 - The top margin's size, in pixels. Write-only. + * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. Text + * is clipped to the bounds. Write-only. + * @property {number} font.size=18 - The size of the text, in pixels. Write-only. + * @property {number} lineHeight=18 - The height of a line of text, in pixels. Write-only. + * @property {Color} color=255,255,255 - The color of the text. Synonym: textColor. Write-only. + * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. + * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. Write-only. + * @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. Write-only. + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * Write-only. + */ + TextOverlay::TextOverlay() : QmlOverlay(URL) { } TextOverlay::TextOverlay(const TextOverlay* textOverlay) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d418a79fbf..f023ac5ef5 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -557,6 +557,64 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { _mayNeedResize = true; } +// Web3DOverlay overrides the meaning of Planar3DOverlay's dimensions property. +/**jsdoc + * These are the properties of a web3d {@link Overlays.OverlayType|OverlayType}. + * @typedef {object} Overlays.Web3DProperties + * + * @property {string} type=web3d - Has the value "web3d". Read-only. + * @property {Color} color=255,255,255 - The color of the overlay. + * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. + * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. + * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * pulseMin to pulseMax, then pulseMax to pulseMin in one period. + * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the + * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the + * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied + * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) + * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and + * rotating as you move your avatar. + * + * @property {string} name="" - A friendly name for the overlay. + * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and + * start. + * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as position. + * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. + * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a + * parentID set, otherwise the same value as rotation. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, + * filled, and filed. Antonyms: isWire and wire. + * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will + * be removed. + * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * dashed. + * @property {boolean} ignoreRayIntersection=false - If true, + * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. + * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't + * have drawInFront set to true and in front of entities. + * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. + * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if + * parentID is an avatar skeleton. A value of 65535 means "no joint". + * + * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * parallel to the user's avatar's "up" direction. + * + * @property {string} url - The URL of the Web page to display. + * @property {string} scriptURL="" - The URL of a file of JavaScript to inject into the Web page. + * @property {Vec2} resolution=640,480 - The number of pixels + * @property {number} dpi=30.47 - The dots per inch to display the Web page at. + * @property {Vec2} dimensions=1,1 - Scales the size of the overlay calculated from resolution and + * dpi. Synonyms: scale, size. + * @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second. + * @property {boolean} showKeyboardFocusHighlight=true - If true, the Web overlay is highlighted when it has + * keyboard focus. + * @property {string} inputMode=Touch - The user input mode to use - either "Touch" or "Mouse". + */ QVariant Web3DOverlay::getProperty(const QString& property) { if (property == "url") { return _url; From 06012d9dfc2a60a69748b1362a82d8b2d1b08f10 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 16:18:40 +1300 Subject: [PATCH 064/137] Add Color and Rect API types JSDoc --- libraries/shared/src/RegisteredMetaTypes.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 7d0df3ac78..fd5237a29a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -563,6 +563,14 @@ QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { return obj; } +/**jsdoc + * Defines a rectangular portion of an image or screen. + * @typedef {object} Rect + * @property {number} x - Integer left, x-coordinate value. + * @property {number} y - Integer top, y-coordinate value. + * @property {number} width - Integer width of the rectangle. + * @property {number} height - Integer height of the rectangle. + */ QVariant qRectToVariant(const QRect& rect) { QVariantMap obj; obj["x"] = rect.x(); @@ -615,7 +623,13 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color) { } } - +/**jsdoc + * An RGB color value. + * @typedef {object} Color + * @property {number} red - Red component value. Integer in the range 0 - 255. + * @property {number} green - Green component value. Integer in the range 0 - 255. + * @property {number} blue - Blue component value. Integer in the range 0 - 255. + */ QVariant xColorToVariant(const xColor& color) { QVariantMap obj; obj["red"] = color.red; From 73f5841c9f1a9bc276fc2967bbf276a23d6623b8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 16:18:57 +1300 Subject: [PATCH 065/137] Add PointerEvent and KeyboardModifiers API types JSDoc --- libraries/shared/src/PointerEvent.cpp | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index e35832391d..f4da41bcbc 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -41,6 +41,64 @@ PointerEvent::PointerEvent(EventType type, uint32_t id, ; } +/**jsdoc + * A PointerEvent defines a 2D or 3D mouse or similar pointer event. + * @typedef {object} PointerEvent + * @property {string} type - The type of event: "Press", "DoublePress", "Release", or + * "Move". + * @property {number} id - Integer number used to identify the pointer: 0 = hardware mouse, 1 = left + * controller, 2 = right controller. + * @property {Vec2} pos2D - The 2D position of the event on the intersected overlay or entity XY plane. + * @property {Vec3) pos3D - The 3D position of the event on the intersected overlay or entity. + * @property {Vec3} normal - The surface normal at the intersection point. + * @property {Vec3} direction - The direction of the intersection ray. + * @property {string} button - The name of the button pressed: None, Primary, Secondary, + * or Tertiary. + * @property {boolean} isPrimaryButton - true if the button pressed was the primary button, otherwise + * undefined; + * @property {boolean} isLeftButton - true if the button pressed was the primary button, otherwise + * undefined; + * @property {boolean} isSecondaryButton - true if the button pressed was the secondary button, otherwise + * undefined; + * @property {boolean} isRightButton - true if the button pressed was the secondary button, otherwise + * undefined; + * @property {boolean} isTertiaryButton - true if the button pressed was the tertiary button, otherwise + * undefined; + * @property {boolean} isMiddleButton - true if the button pressed was the tertiary button, otherwise + * undefined; + * @property {boolean} isPrimaryHeld - true if the primary button is currently being pressed, otherwise + * false + * @property {boolean} isSecondaryHeld - true if the secondary button is currently being pressed, otherwise + * false + * @property {boolean} isTertiaryHeld - true if the tertiary button is currently being pressed, otherwise + * false + * @property {KeyboardModifiers} keyboardModifiers - Integer value being and with bits set according to which keyboard modifier + * keys are were pressed when the event was generated. + */ +/**jsdoc + *

A KeyboardModifiers value is used to specify which modifier keys on the keyboard are pressed. The value is the sum + * (bitwise OR) of the relevant combination of values from the following table:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
KeyHexadecimal valueDecimal valueDescription
Shift0x0200000033554432A Shift key on the keyboard is pressed.
Control0x0400000067108864A Control key on the keyboard is pressed.
Alt0x08000000134217728An Alt key on the keyboard is pressed.
Meta0x10000000268435456A Meta or Windows key on the keyboard is pressed.
Keypad0x20000000536870912A keypad button is pressed.
Group0x400000001073741824X11 operating system only: An AltGr / Mode_switch key on the keyboard is pressed.
+ * @typedef {number} KeyboardModifiers + */ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) { QScriptValue obj = engine->newObject(); From 167bc2178472703eb922c0884f5a2f84a6fb7e5d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 17:23:53 +1300 Subject: [PATCH 066/137] Fix up PickRay and StylusTip JSDoc --- libraries/shared/src/RegisteredMetaTypes.h | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 7765f18090..be492bcdee 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -144,13 +144,6 @@ public: */ class PickRay : public MathPick { public: - /**jsdoc - * The mathematical definition of a ray. - * - * @typedef {Object} PickRay - * @property {Vec3} origin The origin of the ray. - * @property {Vec3} direction The direction of the ray. - */ PickRay() : origin(NAN), direction(NAN) { } PickRay(const QVariantMap& pickVariant) : origin(vec3FromVariant(pickVariant["origin"])), direction(vec3FromVariant(pickVariant["direction"])) {} PickRay(const glm::vec3& origin, const glm::vec3 direction) : origin(origin), direction(direction) {} @@ -174,17 +167,17 @@ Q_DECLARE_METATYPE(PickRay) QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay); void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); +/**jsdoc + * A StylusTip defines the tip of a stylus. + * + * @typedef {object} StylusTip + * @property {number} side - The hand the tip is attached to: 0 for left, 1 for right. + * @property {Vec3} position - The position of the stylus tip. + * @property {Quat} orientation - The orientation of the stylus tip. + * @property {Vec3} velocity - The velocity of the stylus tip. + */ class StylusTip : public MathPick { public: - /**jsdoc - * The mathematical definition of a stylus tip. - * - * @typedef {Object} StylusTip - * @property {number} side The hand the tip is attached to. 0 == left, 1 == right. - * @property {Vec3} position The position of the tip. - * @property {Quat} orientation The orientation of the tip. - * @property {Vec3} velocity The velocity of the tip. - */ StylusTip() : position(NAN), velocity(NAN) {} StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])), orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} @@ -216,9 +209,9 @@ namespace std { template inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); - hash_combine(seed, rest...); + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hash_combine(seed, rest...); } template <> From 9983e1fd1c55999ace08689878b11b61b386c211 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 18:06:27 +1300 Subject: [PATCH 067/137] Polish Paths JSDoc --- libraries/shared/src/PathUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 320865d667..c94ae591a5 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -24,8 +24,8 @@ * @namespace Paths * @deprecated The Paths API is deprecated. Use {@link Script.resolvePath} and {@link Script.resourcesPath} instead. * @readonly - * @property {string} defaultScripts The path to the scripts directory. Readonly - * @property {string} resources The path to the resources directory. Readonly + * @property {string} defaultScripts - The path to the scripts directory. Read-only. + * @property {string} resources - The path to the resources directory. Read-only. */ class PathUtils : public QObject, public Dependency { Q_OBJECT From 16dd9e0063e78d0e3dce0dda23bfbc2e0347663e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 18:24:38 +1300 Subject: [PATCH 068/137] Move Camera mode JSDoc to be a typedef --- libraries/shared/src/shared/Camera.cpp | 46 ++++++++++++++++++++ libraries/shared/src/shared/Camera.h | 60 ++------------------------ 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index ced50f7760..76e5e770bb 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -10,6 +10,52 @@ #include "Camera.h" +/**jsdoc + *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the + * following modes:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ModeStringDescription
First Person"first person"The camera is positioned such that you have the same view as your avatar. The camera moves and rotates with your + * avatar.
Third Person"third person"The camera is positioned such that you have a view from just behind your avatar. The camera moves and rotates with + * your avatar.
Mirror"mirror"The camera is positioned such that you are looking directly at your avatar. The camera moves and rotates with your + * avatar.
Independent"independent"The camera's position and orientation don't change with your avatar movement. Instead, they can be set via + * scripting.
Entity"entity"The camera's position and orientation are set to be the same as a specified entity's, and move with the entity as + ( it moves. + *
+ * @typedef {string} Camera.Mode + */ CameraMode stringToMode(const QString& mode) { if (mode == "third person") { return CAMERA_MODE_THIRD_PERSON; diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 83f1174244..262e3cb14b 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -37,65 +37,13 @@ class Camera : public QObject { Q_OBJECT /**jsdoc - * The Camera API provides access to the "camera" that defines your view in desktop and HMD modes. - * - *

- * - *

Modes:
- * - *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the - * following modes:

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
ModeStringDescription
First Person"first person"The camera is positioned such that you have the same view as your avatar. The camera moves and rotates with - * your avatar.
Third Person"third person"The camera is positioned such that you have a view from just behind your avatar. The camera moves and rotates - * with your avatar.
Mirror"mirror"The camera is positioned such that you are looking directly at your avatar. The camera moves and rotates with - * your avatar.
Independent"independent"The camera's position and orientation don't change with your avatar movement. Instead, they can be set via - * scripting.
Entity"entity"The camera's position and orientation are set to be the same as a specified entity's, and move with the entity - * as it moves. - *
- * - *

The camera mode can be changed using Camera.mode and {@link Camera.setModeString}, and Interface's "View" - * menu.

+ * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. * * @namespace Camera * @property position {Vec3} The position of the camera. You can set this value only when the camera is in entity mode. * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in entity * mode. - * @property mode {string} The camera mode. + * @property mode {Camera.Mode} The camera mode. * @property frustum {ViewFrustum} The camera frustum. * @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera * is in entity mode. @@ -130,14 +78,14 @@ public slots: /**jsdoc * Get the current camera mode. You can also get the mode using the Camera.mode property. * @function Camera.getModeString - * @returns {string} The current camera mode. + * @returns {Camera.Mode} The current camera mode. */ QString getModeString() const; /**jsdoc * Set the camera mode. You can also set the mode using the Camera.mode property. * @function Camera.setModeString - * @param {string} mode - The mode to set the camera to. + * @param {Camera.Mode} mode - The mode to set the camera to. */ void setModeString(const QString& mode); From 425e2eb82cb8169a5ff20b55b49440e8933519da Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 25 Nov 2017 18:24:58 +1300 Subject: [PATCH 069/137] Add missing parameter to Camera.newMode() JSDoc --- libraries/shared/src/shared/Camera.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 262e3cb14b..929a593ae2 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -179,6 +179,7 @@ signals: /**jsdoc * Triggered when the camera mode changes. * @function Camera.modeUpdated + * @param {Camera.Mode} newMode - The new camera mode. * @returns {Signal} * @example Report camera mode changes. * function onCameraModeUpdated(newMode) { From 92493f14d7cb308eca7ebea36ca0b551dba1041f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Nov 2017 16:30:09 +1300 Subject: [PATCH 070/137] Miscellaneous JSDoc fixes --- interface/src/ui/overlays/Overlays.h | 6 +++--- libraries/shared/src/PointerEvent.cpp | 2 +- libraries/shared/src/RegisteredMetaTypes.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 6561a99d0a..3b35cda7ea 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -280,9 +280,9 @@ public slots: * @function Overlays.textSize * @param {Uuid} overlayID - The ID of the overlay to use for calculation. * @param {string} text - The string to calculate the size of. - * @returns {Vec2} The size of the text if the overlay is a text overlay, otherwise - * { x: 0, y: 0 }. If the overlay is a 2D overlay the size is in pixels; if the overlay if a 3D overlay the - * size is in meters. + * @returns {Size} The size of the text if the overlay is a text overlay, otherwise + * { height: 0, width : 0 }. If the overlay is a 2D overlay the size is in pixels; if the overlay if a 3D + * overlay the size is in meters. */ QSizeF textSize(OverlayID id, const QString& text); diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index f4da41bcbc..4f4d24639c 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -49,7 +49,7 @@ PointerEvent::PointerEvent(EventType type, uint32_t id, * @property {number} id - Integer number used to identify the pointer: 0 = hardware mouse, 1 = left * controller, 2 = right controller. * @property {Vec2} pos2D - The 2D position of the event on the intersected overlay or entity XY plane. - * @property {Vec3) pos3D - The 3D position of the event on the intersected overlay or entity. + * @property {Vec3} pos3D - The 3D position of the event on the intersected overlay or entity. * @property {Vec3} normal - The surface normal at the intersection point. * @property {Vec3} direction - The direction of the intersection ray. * @property {string} button - The name of the button pressed: None, Primary, Secondary, diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index fd5237a29a..2d4ad288c2 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -826,6 +826,12 @@ void quuidFromScriptValue(const QScriptValue& object, QUuid& uuid) { uuid = fromString; } +/**jsdoc + * A 2D size value. + * @typedef {object} Size + * @property {number} height - The height value. + * @property {number} width - The width value. + */ QScriptValue qSizeFToScriptValue(QScriptEngine* engine, const QSizeF& qSizeF) { QScriptValue obj = engine->newObject(); obj.setProperty("width", qSizeF.width()); From a6937d7c8b2dac41065c9019a4f41aec13c7ac97 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Nov 2017 16:30:25 +1300 Subject: [PATCH 071/137] Add Overlays API examples JSDoc --- interface/src/ui/overlays/Overlays.h | 248 ++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3b35cda7ea..98c9327c1a 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -74,7 +74,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R * The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to * yourself and that aren't persisted to the domain. They are used for UI. * @namespace Overlays - * @property {Uuid} keyboardFocusOverlay - Get or set the [web3d]{@link Overlays.OverlayType} overlay that has keyboard focus. + * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus. * If no overlay is set, get returns null; set null to clear keyboard focus. */ @@ -120,6 +120,13 @@ public slots: * @param {Overlays.OverlayType} type - The type of the overlay to add. * @param {Overlays.OverlayProperties} properties - The properties of the overlay to add. * @returns {Uuid} The ID of the newly created overlay. + * @example Add a cube overlay in front of your avatar. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); */ OverlayID addOverlay(const QString& type, const QVariant& properties); @@ -128,6 +135,20 @@ public slots: * @function Overlays.cloneOverlay * @param {Uuid} overlayID - The ID of the overlay to clone. * @returns {Uuid} The ID of the new overlay. + * @example Add an overlay in front of your avatar, clone it, and move the clone to be above the + * original. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })); + * var original = Overlays.addOverlay("cube", { + * position: position, + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * + * var clone = Overlays.cloneOverlay(original); + * Overlays.editOverlay(clone, { + * position: Vec3.sum({ x: 0, y: 0.5, z: 0}, position) + * }); */ OverlayID cloneOverlay(OverlayID id); @@ -137,6 +158,18 @@ public slots: * @param {Uuid} overlayID - The ID of the overlay to edit. * @param {Overlays.OverlayProperties} properties - The properties changes to make. * @returns {boolean} true if the overlay was found and edited, otherwise false. + * @example Add an overlay in front of your avatar then change its color. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * + * var success = Overlays.editOverlay(overlay, { + * color: { red: 255, green: 0, blue: 0 } + * }); + * print("Success: " + success); */ bool editOverlay(OverlayID id, const QVariant& properties); @@ -146,11 +179,25 @@ public slots: * @param propertiesById {object.} - On object with overlay IDs as keys and * {@link Overlays.OverlayProperties|OverlayProperties} edits to make for each as values. * @returns {boolean} true if all overlays were found and edited, otherwise false. - * @example Demonstrate creating the propertiesById parameter. + * @example Create two overlays in front of your avatar then change their colors. + * var overlayA = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var overlayB = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * * var overlayEdits = {}; - * overlayEdits[overlayA] = propertiesA; - * overlayEdits[overlayB] = propertiesB; + * overlayEdits[overlayA] = { color: { red: 255, green: 0, blue: 0 } }; + * overlayEdits[overlayB] = { color: { red: 0, green: 255, blue: 0 } }; * var success = Overlays.editOverlays(overlayEdits); + * print("Success: " + success); */ bool editOverlays(const QVariant& propertiesById); @@ -158,6 +205,15 @@ public slots: * Delete an overlay. * @function Overlays.deleteOverlay * @param {Uuid} overlayID - The ID of the overlay to delete. + * @example Create an overlay in front of your avatar then delete it. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay: " + overlay); + * Overlays.deleteOverlay(overlay); */ void deleteOverlay(OverlayID id); @@ -166,6 +222,15 @@ public slots: * @function Overlays.getOverlayType * @param {Uuid} overlayID - The ID of the overlay to get the type of. * @returns {Overlays.OverlayType} The type of the overlay if found, otherwise an empty string. + * @example Create an overlay in front of your avatar then get and report its type. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var type = Overlays.getOverlayType(overlay); + * print("Type: " + type); */ QString getOverlayType(OverlayID overlayId); @@ -178,10 +243,21 @@ public slots: QObject* getOverlayObject(OverlayID id); /**jsdoc - * Get the ID of the overlay at a particular point on the screen or HUD. + * Get the ID of the 2D overlay at a particular point on the screen or HUD. * @function Overlays.getOverlayAtPoint * @param {Vec2} point - The point to check for an overlay. - * @returns {Uuid} The ID of the overlay at the specified point if found, otherwise null. + * @returns {Uuid} The ID of the 2D overlay at the specified point if found, otherwise null. + * @example Create a 2D overlay and add an event function that reports the overlay clicked on, if any. + * var overlay = Overlays.addOverlay("rectangle", { + * bounds: { x: 100, y: 100, width: 200, height: 100 }, + * color: { red: 255, green: 255, blue: 255 } + * }); + * print("Created: " + overlay); + * + * Controller.mousePressEvent.connect(function (event) { + * var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + * print("Clicked: " + overlay); + * }); */ OverlayID getOverlayAtPoint(const glm::vec2& point); @@ -191,6 +267,15 @@ public slots: * @param {Uuid} overlayID - The ID of the overlay. * @param {string} property - The name of the property value to get. * @returns {object} The value of the property if the overlay and property can be found, otherwise undefined. + * @example Create an overlay in front of your avatar then report its alpha property value. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var alpha = Overlays.getProperty(overlay, "alpha"); + * print("Overlay alpha: " + alpha); */ OverlayPropertyResult getProperty(OverlayID id, const QString& property); @@ -201,6 +286,15 @@ public slots: * @param {Array.} properties - An array of names of properties to get the values of. * @returns {Overlays.OverlayProperties} The values of valid properties if the overlay can be found, otherwise * undefined. + * @example Create an overlay in front of your avatar then report some of its properties. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var properties = Overlays.getProperties(overlay, ["color", "alpha", "grabbable"]); + * print("Overlay properties: " + JSON.stringify(properties)); */ OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties); @@ -211,6 +305,24 @@ public slots: * properties to get for each as values. * @returns {object.} An object with overlay IDs as keys and * {@link Overlays.OverlayProperties|OverlayProperties} as values. + * @example Create two cube overlays in front of your avatar then get some of their properties. + * var overlayA = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var overlayB = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * var propertiesToGet = {}; + * propertiesToGet[overlayA] = ["color", "alpha"]; + * propertiesToGet[overlayB] = ["dimensions"]; + * var properties = Overlays.getOverlaysProperties(propertiesToGet); + * print("Overlays properties: " + JSON.stringify(properties)); */ OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties); @@ -227,6 +339,20 @@ public slots: * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking * into account overlayIDsToInclude and overlayIDsToExclude if they're not empty. + * @example Create a cube overlay in front of your avatar. Report 3D overlay intersection details for mouse + * clicks. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * + * Controller.mousePressEvent.connect(function (event) { + * var pickRay = Camera.computePickRay(event.x, event.y); + * var intersection = Overlays.findRayIntersection(pickRay); + * print("Intersection: " + JSON.stringify(intersection)); + * }); */ RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, @@ -263,6 +389,24 @@ public slots: * @param {Vec3} center - The center of the search sphere. * @param {number} radius - The radius of the search sphere. * @returns {Uuid[]} An array of overlay IDs with bounding boxes that touch a search sphere. + * @example Create two cube overlays in front of your avatar then search for overlays near your avatar. + * var overlayA = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay A: " + overlayA); + * var overlayB = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay B: " + overlayB); + * + * var overlaysFound = Overlays.findOverlays(MyAvatar.position, 5.0); + * print("Overlays found: " + JSON.stringify(overlaysFound)); */ QVector findOverlays(const glm::vec3& center, float radius); @@ -272,6 +416,15 @@ public slots: * @function Overlays.isLoaded * @param {Uuid} overlayID - The ID of the overlay to check. * @returns {boolean} true if the overlay's assets have been loaded, otherwise false. + * @example Create an image overlay and report whether its image is loaded after 1s. + * var overlay = Overlays.addOverlay("image", { + * bounds: { x: 100, y: 100, width: 200, height: 200 }, + * imageURL: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png" + * }); + * Script.setTimeout(function () { + * var isLoaded = Overlays.isLoaded(overlay); + * print("Image loaded: " + isLoaded); + * }, 1000); */ bool isLoaded(OverlayID id); @@ -283,6 +436,15 @@ public slots: * @returns {Size} The size of the text if the overlay is a text overlay, otherwise * { height: 0, width : 0 }. If the overlay is a 2D overlay the size is in pixels; if the overlay if a 3D * overlay the size is in meters. + * @example Calculate the size of "hello" in a 3D text overlay. + * var overlay = Overlays.addOverlay("text3d", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })), + * rotation: MyAvatar.orientation, + * text: "hello", + * lineHeight: 0.2 + * }); + * var textSize = Overlays.textSize(overlay, "hello"); + * print("Size of \"hello\": " + JSON.stringify(textSize)); */ QSizeF textSize(OverlayID id, const QString& text); @@ -337,6 +499,38 @@ public slots: * @function Overlays.sendMousePressOnOverlay * @param {Uuid} overlayID - The ID of the overlay to generate a mouse press event on. * @param {PointerEvent} event - The mouse press event details. + * @example Create a 2D rectangle overlay plus a 3D cube overlay and generate mousePressOnOverlay events for the 2D + * overlay. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("3D overlay: " + overlay); + * + * var overlay = Overlays.addOverlay("rectangle", { + * bounds: { x: 100, y: 100, width: 200, height: 100 }, + * color: { red: 255, green: 255, blue: 255 } + * }); + * print("2D overlay: " + overlay); + * + * // Overlays.mousePressOnOverlay by default applies only to 3D overlays. + * Overlays.mousePressOnOverlay.connect(function(overlayID, event) { + * print("Clicked: " + overlayID); + * }); + * + * Controller.mousePressEvent.connect(function (event) { + * // Overlays.getOverlayAtPoint applies only to 2D overlays. + * var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + * if (overlay) { + * Overlays.sendMousePressOnOverlay(overlay, { + * type: "press", + * id: 0, + * pos2D: event + * }); + * } + * }); */ void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); @@ -383,7 +577,7 @@ public slots: /**jsdoc * Get the ID of the Web3D overlay that has keyboard focus. * @function Overlays.getKeyboardFocusOverlay - * @returns {Uuid} The ID of the [web3d]{@link Overlays.OverlayType} overlay that has focus, if any, otherwise + * @returns {Uuid} The ID of the {@link Overlays.OverlayType|web3d} overlay that has focus, if any, otherwise * null. */ OverlayID getKeyboardFocusOverlay(); @@ -391,7 +585,7 @@ public slots: /**jsdoc * Set the Web3D overlay that has keyboard focus. * @function Overlays.setKeyboardFocusOverlay - * @param {Uuid} overlayID - The ID of the [web3d]{@link Overlays.OverlayType} overlay to set keyboard focus to. Use + * @param {Uuid} overlayID - The ID of the {@link Overlays.OverlayType|web3d} overlay to set keyboard focus to. Use * {@link Uuid|Uuid.NULL} or null to unset keyboard focus from an overlay. */ void setKeyboardFocusOverlay(const OverlayID& id); @@ -402,6 +596,21 @@ signals: * @function Overlays.overlayDeleted * @param {Uuid} overlayID - The ID of the overlay that was deleted. * @returns {Signal} + * @example Create an overlay then delete it after 1s. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay: " + overlay); + * + * Overlays.overlayDeleted.connect(function(overlayID) { + * print("Deleted: " + overlayID); + * }); + * Script.setTimeout(function () { + * Overlays.deleteOverlay(overlay); + * }, 1000); */ void overlayDeleted(OverlayID id); @@ -416,6 +625,17 @@ signals: * @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on. * @param {PointerEvent} event - The mouse press event details. * @returns {Signal} + * // Create a cube overlay in front of your avatar and report mouse clicks on it. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay: " + overlay); + * Overlays.mousePressOnOverlay.connect(function(overlayID, event) { + * print("Clicked: " + overlayID); + * }); */ void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); @@ -469,6 +689,18 @@ signals: * @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on. * @param {PointerEvent} event - The mouse move event details. * @returns {Signal} + * @example Create a cube overlay in front of your avatar and report when you start hovering your mouse over + * it. + * var overlay = Overlays.addOverlay("cube", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + * solid: true + * }); + * print("Overlay: " + overlay); + * Overlays.hoverEnterOverlay.connect(function(overlayID, event) { + * print("Hover enter: " + overlayID); + * }); */ void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event); From 97b4dcaa5c0241d526f09cfdc039186bd731b90f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Nov 2017 16:31:13 +1300 Subject: [PATCH 072/137] Web3DOverlay's resolution property is deprecated --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f023ac5ef5..5a0c2ec40d 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -606,7 +606,7 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * * @property {string} url - The URL of the Web page to display. * @property {string} scriptURL="" - The URL of a file of JavaScript to inject into the Web page. - * @property {Vec2} resolution=640,480 - The number of pixels + * @property {Vec2} resolution=640,480 - The number of pixels. Deprecated: This property is being removed. * @property {number} dpi=30.47 - The dots per inch to display the Web page at. * @property {Vec2} dimensions=1,1 - Scales the size of the overlay calculated from resolution and * dpi. Synonyms: scale, size. From d607ca0914445d26e9d23bbeed17517c0ba3a315 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 28 Nov 2017 11:04:48 -0800 Subject: [PATCH 073/137] initialize the _lastAnimated time when the modelEntityRenderer is instantiated, this fixes the sync between interface and the server --- .../src/RenderableModelEntityItem.cpp | 157 +++++++++++------- .../src/RenderableModelEntityItem.h | 7 +- libraries/entities/src/ModelEntityItem.cpp | 133 ++++++++++----- libraries/entities/src/ModelEntityItem.h | 3 +- 4 files changed, 190 insertions(+), 110 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ba0272ed2a..fa24a171f1 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -63,13 +63,17 @@ bool ModelEntityWrapper::isModelLoaded() const { EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; } RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) : ModelEntityWrapper(entityItemID), _dimensionsInitialized(dimensionsInitialized) { + + } RenderableModelEntityItem::~RenderableModelEntityItem() { } @@ -987,34 +991,22 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { } //get entity model anim props - bool isLooping = entity->getAnimationLoop(); - int firstFrame = entity->getAnimationFirstFrame(); - int lastFrame = entity->getAnimationLastFrame(); - bool isHolding = entity->getAnimationHold(); - int updatedFrameCount = lastFrame - firstFrame + 1; - - //get the updated frame from the ModelEntity - auto modelAnimProperties = entity->getAnimationProperties(); - - //_currentFrame = modelAnimProperties.getCurrentFrame(); + int updatedFrameCount = entity->getAnimationLastFrame() - entity->getAnimationFirstFrame() + 1; + - //tempbool = modelAnimProperties.getRunning(); - //qCDebug(entitiesrenderer) << "is playing is: " << tempbool; - - qCDebug(entitiesrenderer) << "the client frame count is the following " << _currentFrame; - - if ((firstFrame >= 0) && (firstFrame < lastFrame) && (lastFrame <= frameCount)) { - //length of animation in now determined by first and last frame - updatedFrameCount = (lastFrame - firstFrame + 1); + if ((entity->getAnimationFirstFrame() < 0) && (entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())){// && (lastFrame <= frameCount)) { + //we don't increment currentframe if the first frame is < zero or > than last frame. + //return; } + if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; } - + auto now = usecTimestampNow(); //find out how long it has been since this animation started. @@ -1022,46 +1014,38 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { auto interval = now - _lastAnimated; _lastAnimated = now; - float oldCurrentFrame = _currentFrame; - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); + //here we implement the looping animation property //if we have played through the animation once then we hold on the last frame - - if( isLooping || (_currentFrame < _renderAnimationProperties.getLastFrame() ) ){ - //else advance the current frame. - //if hold or not playing don't advance the current frame. - //also if the animFrame is outside of first or last frame then don't advance the motion. - if (!isHolding && entity->getAnimationIsPlaying() && !( _renderAnimationProperties.getCurrentFrame() > _renderAnimationProperties.getLastFrame() ) && !( _renderAnimationProperties.getCurrentFrame() < _renderAnimationProperties.getFirstFrame() ) ) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); - while ((_currentFrame - _renderAnimationProperties.getFirstFrame()) > updatedFrameCount) { - _currentFrame -= updatedFrameCount; + if (!(entity->getAnimationHold()) && entity->getAnimationIsPlaying()) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * entity->getAnimationFPS()); + if (_currentFrame > entity->getAnimationLastFrame()) { + if (entity->getAnimationLoop()) { + while ((_currentFrame - entity->getAnimationFirstFrame()) > (updatedFrameCount - 1)) { + _currentFrame -= (updatedFrameCount - 1); + } + }else{ + _currentFrame = entity->getAnimationLastFrame(); + } + }else if (_currentFrame < entity->getAnimationFirstFrame()) { + if (entity->getAnimationFirstFrame() < 0) { + _currentFrame = 0; + }else { + _currentFrame = entity->getAnimationFirstFrame(); } } - else { - //use old currentFrame - _currentFrame = oldCurrentFrame; - } - }else { - //make current frame the endanim frame - _currentFrame = _renderAnimationProperties.getLastFrame(); } - //save the last place that we rendered ourselves. - //entity->setAnimationCurrentFrame(_currentFrame); - - { + qCDebug(entitiesrenderer) << "_currentFrame " << _currentFrame; + + + { //where are we in the currently defined animation segment? - // int animationCurrentFrame = (int)(glm::floor(_currentFrame - firstFrame)) % updatedFrameCount; - //this gives us the absolute frame value to use by adding the first frame value. - // animationCurrentFrame += firstFrame; int animationCurrentFrame = (int)(glm::floor(_currentFrame)); - - - - - + //in the case where the last frame is greater than the framecount then clamp + //it to the end of the animation until it loops around. + if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; } @@ -1382,23 +1366,65 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // make a copy of the animation properites auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { - qCDebug(entitiesrenderer) << "this is where the change is currently handled in the rendering code"; - qCDebug(entitiesrenderer) << "getting the currently playing frame from the modelentityitem update" << newAnimationProperties.getCurrentFrame(); withWriteLock([&] { - if ( (newAnimationProperties.getCurrentFrame() != _renderAnimationProperties.getCurrentFrame()) || (newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { - if (!(newAnimationProperties.getCurrentFrame() > newAnimationProperties.getLastFrame()) && !(newAnimationProperties.getCurrentFrame() < newAnimationProperties.getFirstFrame())) { + if ((newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { + if (_currentFrame < 0) { + qCDebug(entitiesrenderer) << "point A before assign" << _currentFrame; _currentFrame = newAnimationProperties.getCurrentFrame();// +((float)newAnimationProperties.getCurrentlyPlayingFrame() / (float)USECS_PER_SECOND)*(newAnimationProperties.getFPS()); - _endAnim = _currentFrame + ( newAnimationProperties.getLastFrame() - newAnimationProperties.getFirstFrame() ); - _lastAnimated = 0; + qCDebug(entitiesrenderer) << "point A after assign" << _currentFrame; + qCDebug(entitiesrenderer) << "current " <getAnimationProperties(); + } else { + //if first frame is less than zero then don't do anything. + if (!(entity->getAnimationFirstFrame() < 0)) { + // if the current frame is less than zero, this is from the initialization. + if (_currentFrame < 0) { + qCDebug(entitiesrenderer) << "point D property current frame " << entity->getName() << newAnimationProperties.getCurrentFrame(); + if ((newAnimationProperties.getCurrentFrame() < newAnimationProperties.getLastFrame()) && (newAnimationProperties.getCurrentFrame() > newAnimationProperties.getFirstFrame())) { + + _currentFrame = newAnimationProperties.getCurrentFrame(); + qCDebug(entitiesrenderer) << "point D.1 " << _currentFrame; + qCDebug(entitiesrenderer) << "last animated " << _lastAnimated; + _lastAnimated = usecTimestampNow(); + } + else { + _currentFrame = newAnimationProperties.getFirstFrame(); + qCDebug(entitiesrenderer) << "point D.2 " << _currentFrame; + _lastAnimated = usecTimestampNow(); + qCDebug(entitiesrenderer) << entity->getName() << "last animated " << _lastAnimated; + } + } + } + } } //angus @@ -1410,9 +1436,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } - animate(entity); + if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { + animate(entity); + } emit requestRenderUpdate(); } + } void ModelEntityRenderer::flagForCollisionGeometryUpdate() { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index fdb0cf5d2c..7f3ee47534 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -133,7 +133,10 @@ class ModelEntityRenderer : public TypedEntityRenderergetAnimationProperties(); if (_previousAnimationProperties != currentAnimationProperties) { - qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code"; + //qCDebug(entities) << "properties changed in modelentity code" << _currentFrame; withWriteLock([&] { - _previousAnimationProperties = currentAnimationProperties; + if ( (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { - // if (!(currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getLastFrame()) && !(currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getFirstFrame())) { - // _currentlyPlayingFrame = currentAnimationProperties.getCurrentFrame(); - //_endAnim = _currentlyPlayingFrame + ( currentAnimationProperties.getLastFrame() - currentAnimationProperties.getFirstFrame() ); - //_lastAnimated = 0; - // } - qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, current frame is: \n\n" << currentAnimationProperties.getCurrentFrame(); + _lastAnimated = usecTimestampNow(); + _currentFrame = currentAnimationProperties.getFirstFrame(); + qCDebug(entities) << "point 2 " << _currentFrame; setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); - } - else { - //else if ( _previousAnimationProperties.getLoop() && !currentAnimationProperties.getLoop()) { - // int currentframe_mod_length = (int)(_currentlyPlayingFrame - (int)(glm::floor(currentAnimationProperties.getCurrentFrame()))) % ((int)(glm::floor(currentAnimationProperties.getLastFrame())) - (int)(glm::floor(currentAnimationProperties.getFirstFrame())) + 1); - //_endAnim = _currentlyPlayingFrame + ((int)(currentAnimationProperties.getLastFrame()) - (int)(currentAnimationProperties.getFirstFrame())) - (float)currentframe_mod_length; - //} - setAnimationCurrentFrame(currentAnimationProperties.getCurrentFrame()); + }else if (currentAnimationProperties.getHold() && !_previousAnimationProperties.getHold()) { + //_lastAnimated = 0; + //_currentFrame = currentAnimationProperties.getCurrentFrame(); + qCDebug(entities) << "hold is pressed" << _currentFrame; + }else if (!currentAnimationProperties.getHold() && _previousAnimationProperties.getHold()) { + //_lastAnimated = 0; + //_currentFrame = currentAnimationProperties.getCurrentFrame(); + qCDebug(entities) << "hold is unpressed" << _currentFrame; + }else if (!currentAnimationProperties.getLoop() && _previousAnimationProperties.getLoop()) { + //_lastAnimated = 0; + qCDebug(entities) << "loop is unpressed" << _currentFrame; + }else if (currentAnimationProperties.getLoop() && !_previousAnimationProperties.getLoop()) { + //_lastAnimated = 0; + qCDebug(entities) << "loop is pressed" << _currentFrame; + }else if(currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()){ + _currentFrame = currentAnimationProperties.getCurrentFrame(); + // if (_currentFrame < currentAnimationProperties.getFirstFrame()) { + // _currentFrame = currentAnimationProperties.getFirstFrame(); + // } + // current frame greater than lastframe is dealt with in updateframe. + //_lastAnimated = usecTimestampNow(); + qCDebug(entities) << "point 3 " << _currentFrame; } }); - - qCDebug(entities) << "this is where the _currentFrame change is handled in the ModelEntityItem.cpp code, current frame is: " << currentAnimationProperties.getCurrentFrame(); + _previousAnimationProperties = this->getAnimationProperties(); + //qCDebug(entities) << "point 4 " << _currentFrame; + } + else { + + //if the first frame is less than zero that is an error, so don't do anything. + if (!(getAnimationFirstFrame() < 0)) { + //if the current frame is less than zero then we are restarting the server. + if (_currentFrame < 0) { + qCDebug(entities) << "point 3.5 " << _currentFrame; + //_previousAnimationProperties = currentAnimationProperties; + if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { + _currentFrame = currentAnimationProperties.getCurrentFrame(); + } + else { + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(_currentFrame); + _lastAnimated = 0; + } + } + } } //_previousAnimationProperties = currentAnimationProperties; - updateFrameCount(); - + if (isAnimatingSomething()) { + if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame()) ) { + updateFrameCount(); + } + } } } @@ -252,9 +286,6 @@ void ModelEntityItem::updateFrameCount() { auto interval = now - _lastAnimated; _lastAnimated = now; - - - //here we implement the looping animation property //get entity anim props @@ -263,28 +294,37 @@ void ModelEntityItem::updateFrameCount() { int lastFrame = getAnimationLastFrame(); bool isHolding = getAnimationHold(); int updatedFrameCount = lastFrame - firstFrame + 1; - - if (isLooping || (_currentFrame < _previousAnimationProperties.getLastFrame())) { - //else advance the current frame. - //if hold or not playing don't advance the current frame. - //also if the animFrame is outside of first or last frame then don't advance the motion. - if (!isHolding && getAnimationIsPlaying() && !(_previousAnimationProperties.getCurrentFrame() > _previousAnimationProperties.getLastFrame()) && !(_previousAnimationProperties.getCurrentFrame() < _previousAnimationProperties.getFirstFrame())) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _previousAnimationProperties.getFPS()); - while ((_currentFrame - _previousAnimationProperties.getFirstFrame()) > updatedFrameCount) { - _currentFrame -= updatedFrameCount; - } - qCDebug(entities) << "the frame is now 1 " << _currentFrame; - // setAnimationCurrentlyPlayingFrame(_currentlyPlayingFrame); - setAnimationCurrentFrame(_currentFrame); - } - - } else { - _currentFrame = getAnimationLastFrame(); - setAnimationCurrentFrame(_currentFrame); - qCDebug(entities) << "the frame is now 2 " << _currentFrame; - } + + + + //qCDebug(entities) << "point 5 " << _currentFrame; + + if (!isHolding && getAnimationIsPlaying()) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * getAnimationFPS()); + if (_currentFrame > getAnimationLastFrame()) { + if (isLooping) { + while ((_currentFrame - getAnimationFirstFrame()) > (updatedFrameCount - 1)) { + _currentFrame -= (updatedFrameCount - 1); + } + } + else { + _currentFrame = getAnimationLastFrame(); + } + } + else if (_currentFrame < getAnimationFirstFrame()) { + if (getAnimationFirstFrame() < 0) { + _currentFrame = 0; + } + else { + _currentFrame = getAnimationFirstFrame(); + } + } + setAnimationCurrentFrame(_currentFrame); + } + + //qCDebug(entities) << "_currentFrame is " << _currentFrame; } //angus @@ -706,6 +746,13 @@ float ModelEntityItem::getAnimationCurrentFrame() const { }); } +float ModelEntityItem::getAnimationFPS() const { + return resultWithReadLock([&] { + return _animationProperties.getFPS(); + }); +} + + //angus change bool ModelEntityItem::isAnimatingSomething() const { return resultWithReadLock([&] { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 973f49aec4..3dd30761ec 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -108,6 +108,7 @@ public: bool getAnimationIsPlaying() const; float getAnimationCurrentFrame() const; + float getAnimationFPS() const; bool isAnimatingSomething() const; quint64 getCurrentlyPlayingFrame() const; @@ -178,7 +179,7 @@ private: AnimationPropertyGroup _previousAnimationProperties; bool _propTestFlag{ true }; bool _propTestFlag2{ true }; - float _currentFrame{ 0 }; + float _currentFrame{ -1 }; //angus }; From 101e67498ad643f4fedb053f589e6075e57c7991 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 28 Nov 2017 14:11:56 -0800 Subject: [PATCH 074/137] cleaned up the code to remove _currentFrame updating in RenderModelEntityItem also removed the unnessary currently playing frame property --- .../src/EntityTreeRenderer.cpp | 5 +- .../src/RenderableModelEntityItem.cpp | 123 +----------------- .../src/RenderableModelEntityItem.h | 6 +- .../entities/src/AnimationPropertyGroup.cpp | 24 ---- .../entities/src/AnimationPropertyGroup.h | 1 - .../entities/src/EntityItemProperties.cpp | 1 - libraries/entities/src/EntityPropertyFlags.h | 2 - libraries/entities/src/ModelEntityItem.cpp | 82 ++++-------- libraries/entities/src/ModelEntityItem.h | 10 +- 9 files changed, 35 insertions(+), 219 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e3bbfca6cd..5dc1308ae3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -402,8 +402,9 @@ void EntityTreeRenderer::update(bool simulate) { PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); - //angus - //tree->update(simulate); + + //here we update _currentFrame and _lastAnimated and sync with the server properties. + tree->update(simulate); // Update the rendereable entities as needed { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index fa24a171f1..5170596741 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -990,59 +990,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - //get entity model anim props - - int updatedFrameCount = entity->getAnimationLastFrame() - entity->getAnimationFirstFrame() + 1; - - - if ((entity->getAnimationFirstFrame() < 0) && (entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())){// && (lastFrame <= frameCount)) { - //we don't increment currentframe if the first frame is < zero or > than last frame. - //return; - } - - - - if (!_lastAnimated) { - _lastAnimated = usecTimestampNow(); - return; - } - - auto now = usecTimestampNow(); - - //find out how long it has been since this animation started. - - auto interval = now - _lastAnimated; - _lastAnimated = now; - - - - //here we implement the looping animation property - //if we have played through the animation once then we hold on the last frame - if (!(entity->getAnimationHold()) && entity->getAnimationIsPlaying()) { - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * entity->getAnimationFPS()); - if (_currentFrame > entity->getAnimationLastFrame()) { - if (entity->getAnimationLoop()) { - while ((_currentFrame - entity->getAnimationFirstFrame()) > (updatedFrameCount - 1)) { - _currentFrame -= (updatedFrameCount - 1); - } - }else{ - _currentFrame = entity->getAnimationLastFrame(); - } - }else if (_currentFrame < entity->getAnimationFirstFrame()) { - if (entity->getAnimationFirstFrame() < 0) { - _currentFrame = 0; - }else { - _currentFrame = entity->getAnimationFirstFrame(); - } - } - } - qCDebug(entitiesrenderer) << "_currentFrame " << _currentFrame; - - + //the code to find the current frame is now in modelEntityItem.cpp and we access it below -Angus { //where are we in the currently defined animation segment? - int animationCurrentFrame = (int)(glm::floor(_currentFrame)); + int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); //in the case where the last frame is greater than the framecount then clamp //it to the end of the animation until it loops around. @@ -1361,74 +1312,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->updateRenderItems(); } - { - DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); - // make a copy of the animation properites - auto newAnimationProperties = entity->getAnimationProperties(); - if (newAnimationProperties != _renderAnimationProperties) { - withWriteLock([&] { - if ((newAnimationProperties.getFirstFrame() != _renderAnimationProperties.getFirstFrame()) || (newAnimationProperties.getLastFrame() != _renderAnimationProperties.getLastFrame()) || (newAnimationProperties.getRunning() && !_renderAnimationProperties.getRunning())) { - if (_currentFrame < 0) { - qCDebug(entitiesrenderer) << "point A before assign" << _currentFrame; - _currentFrame = newAnimationProperties.getCurrentFrame();// +((float)newAnimationProperties.getCurrentlyPlayingFrame() / (float)USECS_PER_SECOND)*(newAnimationProperties.getFPS()); - qCDebug(entitiesrenderer) << "point A after assign" << _currentFrame; - qCDebug(entitiesrenderer) << "current " <getAnimationProperties(); - } else { - //if first frame is less than zero then don't do anything. - if (!(entity->getAnimationFirstFrame() < 0)) { - // if the current frame is less than zero, this is from the initialization. - if (_currentFrame < 0) { - qCDebug(entitiesrenderer) << "point D property current frame " << entity->getName() << newAnimationProperties.getCurrentFrame(); - if ((newAnimationProperties.getCurrentFrame() < newAnimationProperties.getLastFrame()) && (newAnimationProperties.getCurrentFrame() > newAnimationProperties.getFirstFrame())) { - - _currentFrame = newAnimationProperties.getCurrentFrame(); - qCDebug(entitiesrenderer) << "point D.1 " << _currentFrame; - qCDebug(entitiesrenderer) << "last animated " << _lastAnimated; - _lastAnimated = usecTimestampNow(); - } - else { - _currentFrame = newAnimationProperties.getFirstFrame(); - qCDebug(entitiesrenderer) << "point D.2 " << _currentFrame; - _lastAnimated = usecTimestampNow(); - qCDebug(entitiesrenderer) << entity->getName() << "last animated " << _lastAnimated; - } - } - } - - } - } - //angus - + //The code to deal with the change of properties is now in ModelEntityItem.cpp + //That is where _currentFrame and _lastAnimated are updated. if (_animating) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 7f3ee47534..1ca72ce57d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -133,10 +133,7 @@ class ModelEntityRenderer : public TypedEntityRenderer& out) { if (allowTranslationChanged()) { out << "animation-allowTranslation"; } - if (currentlyPlayingFrameChanged()) { - out << "animation-currentlyPlayingFrame"; - } } @@ -209,8 +196,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - //angus - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, getCurrentlyPlayingFrame()); return true; } @@ -232,7 +217,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, quint64, setCurrentlyPlayingFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); @@ -243,7 +227,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame); processedBytes += bytesRead; @@ -276,7 +259,6 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, currentlyPlayingFrame); return changedProperties; } @@ -291,7 +273,6 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentlyPlayingFrame, getCurrentlyPlayingFrame); } bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -306,7 +287,6 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentlyPlayingFrame, currentlyPlayingFrame, setCurrentlyPlayingFrame); return somethingChanged; } @@ -322,8 +302,6 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_HOLD; requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION; - //angus - requestedProperties += PROP_ANIMATION_CURRENTLY_PLAYING_FRAME; return requestedProperties; } @@ -347,7 +325,6 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, getCurrentlyPlayingFrame()); } int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -367,6 +344,5 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, quint64, setCurrentlyPlayingFrame); return bytesRead; } diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 1a3da52684..54d4ced92f 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -86,7 +86,6 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); - DEFINE_PROPERTY(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, CurrentlyPlayingFrame, currentlyPlayingFrame, quint64, 0); protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 77fa1231f1..108fc14e30 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1148,7 +1148,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, Animation, animation, CurrentlyPlayingFrame, currentlyPlayingFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3b1f4d60d0..35d40b669a 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -40,8 +40,6 @@ enum EntityPropertyList { PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_PLAYING, PROP_ANIMATION_ALLOW_TRANSLATION, - //angus - PROP_ANIMATION_CURRENTLY_PLAYING_FRAME, // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 23b352fc19..4facd1f253 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -33,6 +33,9 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _lastAnimated = usecTimestampNow(); + qCDebug(entities) << "init last animated " << _lastAnimated; + //set the last animated when interface (re)starts _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; _color[0] = _color[1] = _color[2] = 0; @@ -186,52 +189,45 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit } -//angus +//added update for property fix void ModelEntityItem::update(const quint64& now) { - //put something here - //qCDebug(entities) << "model entity item update" << getName() << " " << getEntityItemID(); { auto currentAnimationProperties = this->getAnimationProperties(); if (_previousAnimationProperties != currentAnimationProperties) { - //qCDebug(entities) << "properties changed in modelentity code" << _currentFrame; - withWriteLock([&] { - + + withWriteLock([&] { if ( (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { - _lastAnimated = usecTimestampNow(); - _currentFrame = currentAnimationProperties.getFirstFrame(); - qCDebug(entities) << "point 2 " << _currentFrame; - setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); + if (_currentFrame < 0) { + _currentFrame = currentAnimationProperties.getCurrentFrame(); + setAnimationCurrentFrame(_currentFrame); + qCDebug(entities) << "restart code hit " << _currentFrame; + } + else { + _lastAnimated = usecTimestampNow(); + qCDebug(entities) << "last animated 1" << _lastAnimated; + _currentFrame = currentAnimationProperties.getFirstFrame(); + qCDebug(entities) << "point 2 " << _currentFrame; + setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); + } }else if (currentAnimationProperties.getHold() && !_previousAnimationProperties.getHold()) { - //_lastAnimated = 0; - //_currentFrame = currentAnimationProperties.getCurrentFrame(); qCDebug(entities) << "hold is pressed" << _currentFrame; }else if (!currentAnimationProperties.getHold() && _previousAnimationProperties.getHold()) { - //_lastAnimated = 0; - //_currentFrame = currentAnimationProperties.getCurrentFrame(); qCDebug(entities) << "hold is unpressed" << _currentFrame; }else if (!currentAnimationProperties.getLoop() && _previousAnimationProperties.getLoop()) { - //_lastAnimated = 0; qCDebug(entities) << "loop is unpressed" << _currentFrame; }else if (currentAnimationProperties.getLoop() && !_previousAnimationProperties.getLoop()) { - //_lastAnimated = 0; qCDebug(entities) << "loop is pressed" << _currentFrame; }else if(currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()){ _currentFrame = currentAnimationProperties.getCurrentFrame(); - // if (_currentFrame < currentAnimationProperties.getFirstFrame()) { - // _currentFrame = currentAnimationProperties.getFirstFrame(); - // } - // current frame greater than lastframe is dealt with in updateframe. - //_lastAnimated = usecTimestampNow(); qCDebug(entities) << "point 3 " << _currentFrame; } }); _previousAnimationProperties = this->getAnimationProperties(); - //qCDebug(entities) << "point 4 " << _currentFrame; } else { @@ -243,11 +239,13 @@ void ModelEntityItem::update(const quint64& now) { //_previousAnimationProperties = currentAnimationProperties; if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { _currentFrame = currentAnimationProperties.getCurrentFrame(); + qCDebug(entities) << "current frame less than zero " << _currentFrame; } else { _currentFrame = currentAnimationProperties.getFirstFrame(); setAnimationCurrentFrame(_currentFrame); - _lastAnimated = 0; + _lastAnimated = usecTimestampNow(); + qCDebug(entities) << "last animated 2" << _lastAnimated; } } } @@ -264,9 +262,6 @@ void ModelEntityItem::update(const quint64& now) { bool ModelEntityItem::needsToCallUpdate() const { - - //put something here - //qCDebug(entities) << "needs to call update"; return true; } @@ -282,29 +277,17 @@ void ModelEntityItem::updateFrameCount() { auto now = usecTimestampNow(); //this is now getting the time since the server started the animation. - //auto interval = now - _currentlyPlayingFrame; auto interval = now - _lastAnimated; _lastAnimated = now; - //here we implement the looping animation property - //get entity anim props - bool isLooping = getAnimationLoop(); - int firstFrame = getAnimationFirstFrame(); - int lastFrame = getAnimationLastFrame(); - bool isHolding = getAnimationHold(); - int updatedFrameCount = lastFrame - firstFrame + 1; + int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1; - - - - //qCDebug(entities) << "point 5 " << _currentFrame; - - if (!isHolding && getAnimationIsPlaying()) { + if (!getAnimationHold() && getAnimationIsPlaying()) { float deltaTime = (float)interval / (float)USECS_PER_SECOND; _currentFrame += (deltaTime * getAnimationFPS()); if (_currentFrame > getAnimationLastFrame()) { - if (isLooping) { + if (getAnimationLoop()) { while ((_currentFrame - getAnimationFirstFrame()) > (updatedFrameCount - 1)) { _currentFrame -= (updatedFrameCount - 1); } @@ -327,7 +310,7 @@ void ModelEntityItem::updateFrameCount() { //qCDebug(entities) << "_currentFrame is " << _currentFrame; } -//angus + @@ -678,13 +661,6 @@ void ModelEntityItem::setAnimationCurrentFrame(float value) { }); } -void ModelEntityItem::setAnimationCurrentlyPlayingFrame(quint64 value) { - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; - withWriteLock([&] { - _animationProperties.setCurrentlyPlayingFrame(value); - }); -} - void ModelEntityItem::setAnimationLoop(bool loop) { withWriteLock([&] { _animationProperties.setLoop(loop); @@ -753,7 +729,6 @@ float ModelEntityItem::getAnimationFPS() const { } -//angus change bool ModelEntityItem::isAnimatingSomething() const { return resultWithReadLock([&] { return !_animationProperties.getURL().isEmpty() && @@ -762,15 +737,8 @@ bool ModelEntityItem::isAnimatingSomething() const { }); } -quint64 ModelEntityItem::getCurrentlyPlayingFrame() const { - return resultWithReadLock([&] { - return _currentlyPlayingFrame; - }); -} - int ModelEntityItem::getLastKnownCurrentFrame() const { return resultWithReadLock([&] { return _lastKnownCurrentFrame; }); } -//angus change \ No newline at end of file diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 3dd30761ec..4aa52d4cef 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -47,11 +47,11 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - //angus + //update and needstocallupdate added back for the entity property fix virtual void update(const quint64& now) override; virtual bool needsToCallUpdate() const override; void updateFrameCount(); - //angus + virtual void debugDump() const override; void setShapeType(ShapeType type) override; @@ -87,7 +87,6 @@ public: void setAnimationURL(const QString& url); void setAnimationCurrentFrame(float value); - void setAnimationCurrentlyPlayingFrame(quint64 value); void setAnimationIsPlaying(bool value); void setAnimationFPS(float value); @@ -111,7 +110,6 @@ public: float getAnimationFPS() const; bool isAnimatingSomething() const; - quint64 getCurrentlyPlayingFrame() const; int getLastKnownCurrentFrame() const; static const QString DEFAULT_TEXTURES; @@ -173,12 +171,8 @@ protected: private: //angus - quint64 _currentlyPlayingFrame{ 0 }; - float _endAnim{ 0 }; uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; - bool _propTestFlag{ true }; - bool _propTestFlag2{ true }; float _currentFrame{ -1 }; //angus }; From 778d98f56b8b388b012ad08f72ccfb63a3295ca7 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 28 Nov 2017 18:42:03 -0800 Subject: [PATCH 075/137] made some changes to fix coding standard problems in the code, also added the update for the EntityItem base class to the update function --- .../src/EntityTreeRenderer.cpp | 4 +- .../src/RenderableModelEntityItem.cpp | 16 ++--- .../src/RenderableModelEntityItem.h | 4 +- libraries/entities/src/ModelEntityItem.cpp | 62 ++++++++----------- libraries/entities/src/ModelEntityItem.h | 4 +- 5 files changed, 37 insertions(+), 53 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5dc1308ae3..7103e72f54 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -403,7 +403,7 @@ void EntityTreeRenderer::update(bool simulate) { if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); - //here we update _currentFrame and _lastAnimated and sync with the server properties. + // here we update _currentFrame and _lastAnimated and sync with the server properties. tree->update(simulate); // Update the rendereable entities as needed @@ -738,7 +738,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); if (rayPickResult.intersects && rayPickResult.entity) { - //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; + // qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID, diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5170596741..fa75702b89 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -465,7 +465,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { // TODO: assert we never fall in here when model not fully loaded - //assert(_model && _model->isLoaded()); + // assert(_model && _model->isLoaded()); updateModelBounds(); model->updateGeometry(); @@ -992,10 +992,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { //the code to find the current frame is now in modelEntityItem.cpp and we access it below -Angus { - //where are we in the currently defined animation segment? + // where are we in the currently defined animation segment? int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); - //in the case where the last frame is greater than the framecount then clamp - //it to the end of the animation until it loops around. + // in the case where the last frame is greater than the framecount then clamp + // it to the end of the animation until it loops around. if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; @@ -1032,10 +1032,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { glm::mat4 translationMat; if (allowTranslation) { - if(index < translations.size()){ + if (index < translations.size()) { translationMat = glm::translate(translations[index]); } - } else if (index < animationJointNames.size()){ + } else if (index < animationJointNames.size()) { QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation if (originalFbxIndices.contains(jointName)) { @@ -1312,8 +1312,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->updateRenderItems(); } - //The code to deal with the change of properties is now in ModelEntityItem.cpp - //That is where _currentFrame and _lastAnimated are updated. + // The code to deal with the change of properties is now in ModelEntityItem.cpp + // That is where _currentFrame and _lastAnimated are updated. if (_animating) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 1ca72ce57d..44ee82713d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -33,7 +33,7 @@ namespace render { namespace entities { class ModelEntityRenderer; } } -//#define MODEL_ENTITY_USE_FADE_EFFECT +// #define MODEL_ENTITY_USE_FADE_EFFECT class ModelEntityWrapper : public ModelEntityItem { using Parent = ModelEntityItem; friend class render::entities::ModelEntityRenderer; @@ -185,8 +185,6 @@ private: bool _animating { false }; uint64_t _lastAnimated { 0 }; float _currentFrame { -1 }; - float _endAnim{ 0 }; - bool tempbool{ false }; }; } } // namespace diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4facd1f253..594353ee68 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -34,8 +34,7 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _lastAnimated = usecTimestampNow(); - qCDebug(entities) << "init last animated " << _lastAnimated; - //set the last animated when interface (re)starts + // set the last animated when interface (re)starts _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; _color[0] = _color[1] = _color[2] = 0; @@ -190,79 +189,71 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit -//added update for property fix +// added update for property fix void ModelEntityItem::update(const quint64& now) { - { auto currentAnimationProperties = this->getAnimationProperties(); if (_previousAnimationProperties != currentAnimationProperties) { - withWriteLock([&] { - if ( (currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { if (_currentFrame < 0) { _currentFrame = currentAnimationProperties.getCurrentFrame(); setAnimationCurrentFrame(_currentFrame); - qCDebug(entities) << "restart code hit " << _currentFrame; - } - else { + qCDebug(entities) << "restart code hit last animated is " << _lastAnimated << " now is " << now; + } else { _lastAnimated = usecTimestampNow(); qCDebug(entities) << "last animated 1" << _lastAnimated; _currentFrame = currentAnimationProperties.getFirstFrame(); qCDebug(entities) << "point 2 " << _currentFrame; setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); } - }else if (currentAnimationProperties.getHold() && !_previousAnimationProperties.getHold()) { + } else if (currentAnimationProperties.getHold() && !_previousAnimationProperties.getHold()) { qCDebug(entities) << "hold is pressed" << _currentFrame; - }else if (!currentAnimationProperties.getHold() && _previousAnimationProperties.getHold()) { + } else if (!currentAnimationProperties.getHold() && _previousAnimationProperties.getHold()) { qCDebug(entities) << "hold is unpressed" << _currentFrame; - }else if (!currentAnimationProperties.getLoop() && _previousAnimationProperties.getLoop()) { + } else if (!currentAnimationProperties.getLoop() && _previousAnimationProperties.getLoop()) { qCDebug(entities) << "loop is unpressed" << _currentFrame; - }else if (currentAnimationProperties.getLoop() && !_previousAnimationProperties.getLoop()) { + } else if (currentAnimationProperties.getLoop() && !_previousAnimationProperties.getLoop()) { qCDebug(entities) << "loop is pressed" << _currentFrame; - }else if(currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()){ + } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { _currentFrame = currentAnimationProperties.getCurrentFrame(); qCDebug(entities) << "point 3 " << _currentFrame; } }); _previousAnimationProperties = this->getAnimationProperties(); - } - else { + } else { - //if the first frame is less than zero that is an error, so don't do anything. + // if the first frame is less than zero that is an error, so don't do anything. if (!(getAnimationFirstFrame() < 0)) { - //if the current frame is less than zero then we are restarting the server. + // if the current frame is less than zero then we are restarting the server. if (_currentFrame < 0) { - qCDebug(entities) << "point 3.5 " << _currentFrame; - //_previousAnimationProperties = currentAnimationProperties; if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { _currentFrame = currentAnimationProperties.getCurrentFrame(); - qCDebug(entities) << "current frame less than zero " << _currentFrame; - } - else { + } else { _currentFrame = currentAnimationProperties.getFirstFrame(); setAnimationCurrentFrame(_currentFrame); _lastAnimated = usecTimestampNow(); - qCDebug(entities) << "last animated 2" << _lastAnimated; } } } } - //_previousAnimationProperties = currentAnimationProperties; + // _previousAnimationProperties = currentAnimationProperties; if (isAnimatingSomething()) { - if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame()) ) { + if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) { updateFrameCount(); } } } - + EntityItem::update(now); } bool ModelEntityItem::needsToCallUpdate() const { - return true; + //return isAnimatingSomething() || EntityItem::needsToCallUpdate(); + return true; } void ModelEntityItem::updateFrameCount() { @@ -276,7 +267,7 @@ void ModelEntityItem::updateFrameCount() { auto now = usecTimestampNow(); - //this is now getting the time since the server started the animation. + // this is now getting the time since the server started the animation. auto interval = now - _lastAnimated; _lastAnimated = now; @@ -291,23 +282,20 @@ void ModelEntityItem::updateFrameCount() { while ((_currentFrame - getAnimationFirstFrame()) > (updatedFrameCount - 1)) { _currentFrame -= (updatedFrameCount - 1); } - } - else { + } else { _currentFrame = getAnimationLastFrame(); } - } - else if (_currentFrame < getAnimationFirstFrame()) { + } else if (_currentFrame < getAnimationFirstFrame()) { if (getAnimationFirstFrame() < 0) { _currentFrame = 0; - } - else { + } else { _currentFrame = getAnimationFirstFrame(); } } setAnimationCurrentFrame(_currentFrame); } - //qCDebug(entities) << "_currentFrame is " << _currentFrame; + } @@ -709,7 +697,7 @@ float ModelEntityItem::getAnimationLastFrame() const { return _animationProperties.getLastFrame(); }); } -//angus change + bool ModelEntityItem::getAnimationIsPlaying() const { return resultWithReadLock([&] { return _animationProperties.getRunning(); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 4aa52d4cef..c0ca12c7f9 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -47,7 +47,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - //update and needstocallupdate added back for the entity property fix + // update() and needstocallupdate() added back for the entity property fix virtual void update(const quint64& now) override; virtual bool needsToCallUpdate() const override; void updateFrameCount(); @@ -170,11 +170,9 @@ protected: ShapeType _shapeType = SHAPE_TYPE_NONE; private: - //angus uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; float _currentFrame{ -1 }; - //angus }; #endif // hifi_ModelEntityItem_h From 233d693d76634c9d04f80be699b029e325496a48 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 29 Nov 2017 10:51:04 -0800 Subject: [PATCH 076/137] removed debug print statements and fixed negative FPS handling --- .../src/RenderableModelEntityItem.cpp | 9 +-- libraries/entities/src/ModelEntityItem.cpp | 56 +++++++++---------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index fa75702b89..d5befbe572 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -981,7 +981,6 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QVector jointsData; const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy @@ -990,13 +989,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - //the code to find the current frame is now in modelEntityItem.cpp and we access it below -Angus { - // where are we in the currently defined animation segment? + // the current frame is set on the server in update() in ModelEntityItem.cpp int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); + // in the case where the last frame is greater than the framecount then clamp // it to the end of the animation until it loops around. - if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; } @@ -1314,8 +1312,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // The code to deal with the change of properties is now in ModelEntityItem.cpp // That is where _currentFrame and _lastAnimated are updated. - - if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { @@ -1326,7 +1322,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } emit requestRenderUpdate(); } - } void ModelEntityRenderer::flagForCollisionGeometryUpdate() { diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 594353ee68..260074da68 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -189,48 +189,47 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit -// added update for property fix +// added update function back for property fix void ModelEntityItem::update(const quint64& now) { { auto currentAnimationProperties = this->getAnimationProperties(); if (_previousAnimationProperties != currentAnimationProperties) { - withWriteLock([&] { - if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + withWriteLock([&] { + // if we hit start animation or change the first or last frame then restart the animation + if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || + (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || + (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + + // when we start interface and the property is are set then the current frame is initialized to -1 if (_currentFrame < 0) { + // don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set _currentFrame = currentAnimationProperties.getCurrentFrame(); setAnimationCurrentFrame(_currentFrame); - qCDebug(entities) << "restart code hit last animated is " << _lastAnimated << " now is " << now; } else { _lastAnimated = usecTimestampNow(); - qCDebug(entities) << "last animated 1" << _lastAnimated; _currentFrame = currentAnimationProperties.getFirstFrame(); - qCDebug(entities) << "point 2 " << _currentFrame; setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); } - } else if (currentAnimationProperties.getHold() && !_previousAnimationProperties.getHold()) { - qCDebug(entities) << "hold is pressed" << _currentFrame; - } else if (!currentAnimationProperties.getHold() && _previousAnimationProperties.getHold()) { - qCDebug(entities) << "hold is unpressed" << _currentFrame; - } else if (!currentAnimationProperties.getLoop() && _previousAnimationProperties.getLoop()) { - qCDebug(entities) << "loop is unpressed" << _currentFrame; - } else if (currentAnimationProperties.getLoop() && !_previousAnimationProperties.getLoop()) { - qCDebug(entities) << "loop is pressed" << _currentFrame; } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { + // don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated _currentFrame = currentAnimationProperties.getCurrentFrame(); - qCDebug(entities) << "point 3 " << _currentFrame; + // qCDebug(entities) << "point 3 " << _currentFrame; } }); _previousAnimationProperties = this->getAnimationProperties(); + } else { - - // if the first frame is less than zero that is an error, so don't do anything. + // else the animation properties have not changed. + // if the first frame is less than zero don't do anything. if (!(getAnimationFirstFrame() < 0)) { - // if the current frame is less than zero then we are restarting the server. + + // if the current frame is less than zero then we have restarted the server. if (_currentFrame < 0) { - if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { + if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && + (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { _currentFrame = currentAnimationProperties.getCurrentFrame(); } else { _currentFrame = currentAnimationProperties.getFirstFrame(); @@ -240,38 +239,40 @@ void ModelEntityItem::update(const quint64& now) { } } } - // _previousAnimationProperties = currentAnimationProperties; + if (isAnimatingSomething()) { if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) { updateFrameCount(); } } } + EntityItem::update(now); } bool ModelEntityItem::needsToCallUpdate() const { - //return isAnimatingSomething() || EntityItem::needsToCallUpdate(); return true; } void ModelEntityItem::updateFrameCount() { - if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); return; } - auto now = usecTimestampNow(); - // this is now getting the time since the server started the animation. + // update the interval since the last animation. auto interval = now - _lastAnimated; _lastAnimated = now; - + // if fps is negative then increment timestamp and return. + if (getAnimationFPS() < 0.0) { + return; + } + int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1; if (!getAnimationHold() && getAnimationIsPlaying()) { @@ -298,11 +299,6 @@ void ModelEntityItem::updateFrameCount() { } - - - - - void ModelEntityItem::debugDump() const { qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID(); qCDebug(entities) << " edited ago:" << getEditedAgo(); From 4269cc4004e1189808673f96e14e678e102a9d8a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Nov 2017 09:28:45 +1300 Subject: [PATCH 077/137] Camera API JSDoc fixes --- libraries/shared/src/shared/Camera.cpp | 2 +- libraries/shared/src/shared/Camera.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index 76e5e770bb..787b7bfb1a 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -50,7 +50,7 @@ * Entity * "entity" * The camera's position and orientation are set to be the same as a specified entity's, and move with the entity as - ( it moves. + * it moves. * * * diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 929a593ae2..ea2e9cddab 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -40,8 +40,8 @@ class Camera : public QObject { * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. * * @namespace Camera - * @property position {Vec3} The position of the camera. You can set this value only when the camera is in entity mode. - * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in entity + * @property position {Vec3} The position of the camera. You can set this value only when the camera is in independent mode. + * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in independent * mode. * @property mode {Camera.Mode} The camera mode. * @property frustum {ViewFrustum} The camera frustum. From a01d118c57e32ee6891a180fb22933784f2d5f25 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Nov 2017 16:03:57 +1300 Subject: [PATCH 078/137] Documentation review --- libraries/script-engine/src/Quat.h | 201 +++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 55 deletions(-) diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index ff66ef9d61..a0c1378b23 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -21,16 +21,18 @@ #include /**jsdoc - * A quaternion value. See also the [Quat]{@link Quat(0)} object. + * A quaternion value. See also the {@link Quat(0)|Quat} object. * @typedef {object} Quat - * @property {number} x imaginary component i. - * @property {number} y imaginary component j. - * @property {number} z imaginary component k. - * @property {number} w real component. + * @property {number} x - Imaginary component i. + * @property {number} y - Imaginary component j. + * @property {number} z - Imaginary component k. + * @property {number} w - Real component. */ /**jsdoc * The Quat API provides facilities for generating and manipulating quaternions. + * Quaternions should be used in preference to Euler angles wherever possible because quaternions don't suffer from the problem + * of gimbal lock. * @namespace Quat * @variation 0 * @property IDENTITY {Quat} The identity rotation, i.e., no rotation. @@ -52,6 +54,12 @@ public slots: * @param {Quat} q1 - The first quaternion. * @param {Quat} q2 - The second quaternion. * @returns {Quat} q1 multiplied with q2. + * @example Calculate the orientation of your avatar's right hand in world coordinates. + * var handController = Controller.Standard.RightHand; + * var handPose = Controller.getPoseValue(handController); + * if (handPose.valid) { + * var handOrientation = Quat.multiply(MyAvatar.orientation, handPose.rotation); + * } */ glm::quat multiply(const glm::quat& q1, const glm::quat& q2); @@ -60,15 +68,24 @@ public slots: * @function Quat(0).normalize * @param {Quat} q - The quaternion to normalize. * @returns {Quat} q normalized to have unit length. + * @example Normalize a repeated delta rotation so that maths rounding errors don't accumulate. + * var deltaRotation = Quat.fromPitchYawRollDegrees(0, 0.1, 0); + * var currentRotation = Quat.ZERO; + * while (Quat.safeEulerAngles(currentRotation).y < 180) { + * currentRotation = Quat.multiply(deltaRotation, currentRotation); + * currentRotation = Quat.normalize(currentRotation); + * // Use currentRotatation for something. + * } */ glm::quat normalize(const glm::quat& q); /**jsdoc - * Calculate the conjugate of a quaternion. Synonym for [Quat.inverse]{@link Quat(0).inverse}. + * Calculate the conjugate of a quaternion. For a unit quaternion, its conjugate is the same as its + * {@link Quat(0).inverse|Quat.inverse}. * @function Quat(0).conjugate * @param {Quat} q - The quaternion to conjugate. - * @returns {Quat} The conjugate (inverse) of q. - * @example A quaternion multiplied by its conjugate is a zero rotation. + * @returns {Quat} The conjugate of q. + * @example A unit quaternion multiplied by its conjugate is a zero rotation. * var quaternion = Quat.fromPitchYawRollDegrees(10, 20, 30); * Quat.print("quaternion", quaternion, true); // dvec3(10.000000, 20.000004, 30.000004) * var conjugate = Quat.conjugate(quaternion); @@ -83,10 +100,13 @@ public slots: * the forward direction. The result has zero roll about its forward direction with respect to the given "up" direction. * @function Quat(0).lookAt * @param {Vec3} eye - The eye position. - * @param {Vec3} center - A point that the eye's looking at; the center of the scene. + * @param {Vec3} target - The point to look at. * @param {Vec3} up - The "up" direction. - * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-center vector and the x-axis to - * be the cross product of the eye-to-center and up vectors. + * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-target vector and the x-axis to + * be the cross product of the eye-to-target and up vectors. + * @example Rotate your view in independent mode to look at the world origin upside down. + * Camera.mode = "independent"; + * Camera.orientation = Quat.lookAt(Camera.position, Vec3.ZERO, Vec3.UNIT_NEG_Y); */ glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); @@ -95,16 +115,14 @@ public slots: * direction. The result has zero roll about its forward direction. * @function Quat(0).lookAtSimple * @param {Vec3} eye - The eye position. - * @param {Vec3} center - A point that the eye's looking at; the center of the scene. - * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-center vector and the x-axis to be the - * cross product of the eye-to-center and an "up" vector. The "up" vector is the y-axis unless the eye-to-center vector - * is nearly aligned with it, in which case the x-axis is used as the "up" vector. - * @example Demonstrate that the calculation orients the negative z-axis. - * var eye = Vec3.ZERO; - * var center = { x: 0, y: 0, z: 1 } - * var orientation = Quat.lookAtSimple(eye, center); - * var eulers = Quat.safeEulerAngles(orientation); - * print(JSON.stringify(eulers)); // {"x":0,"y":-180,"z":0} + * @param {Vec3} target - The point to look at. + * @returns {Quat} A quaternion that orients the negative z-axis to point along the eye-to-target vector and the x-axis to be + * the cross product of the eye-to-target and an "up" vector. The "up" vector is the y-axis unless the eye-to-target + * vector is nearly aligned with it (i.e., looking near vertically up or down), in which case the x-axis is used as the + * "up" vector. + * @example Rotate your view in independent mode to look at the world origin. + * Camera.mode = "independent"; + * Camera.orientation = Quat.lookAtSimple(Camera.position, Vec3.ZERO); */ glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); @@ -114,22 +132,39 @@ public slots: * @param {Vec3} v1 - The first vector. * @param {Vec3} v2 - The second vector. * @returns {Quat} The rotation from v1 onto v2. + * @example Apply a change in velocity to an entity and rotate it to face the direction it's travelling. + * var newVelocity = Vec3.sum(entityVelocity, deltaVelocity); + * var properties = { velocity: newVelocity }; + * if (Vec3.length(newVelocity) > 0.001) { + * properties.rotation = Quat.rotationBetween(entityVelocity, newVelocity); + * } + * Entities.editEntity(entityID, properties); + * entityVelocity = newVelocity; */ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); /**jsdoc * Generate a quaternion from a {@link Vec3} of Euler angles in degrees. * @function Quat(0).fromVec3Degrees - * @param {Vec3} vector - A vector of three Euler angles in degrees. + * @param {Vec3} vector - A vector of three Euler angles in degrees, the angles being the rotations about the x, y, and z + * axes. * @returns {Quat} A quaternion created from the Euler angles in vector. + * @example Zero out pitch and roll from an orientation. + * var eulerAngles = Quat.safeEulerAngles(orientation); + * eulerAngles.x = 0; + * eulerAngles.z = 0; + * var newOrientation = Quat.fromVec3Degrees(eulerAngles); */ glm::quat fromVec3Degrees(const glm::vec3& vec3); /**jsdoc * Generate a quaternion from a {@link Vec3} of Euler angles in radians. * @function Quat(0).fromVec3Radians - * @param {Vec3} vector - A vector of three Euler angles in radians. + * @param {Vec3} vector - A vector of three Euler angles in radians, the angles being the rotations about the x, y, and z + * axes. * @returns {Quat} A quaternion created using the Euler angles in vector. + * @example Create a rotation of 180 degrees about the y axis. + * var rotation = Quat.fromVec3Radians({ x: 0, y: Math.PI, z: 0 }); */ glm::quat fromVec3Radians(const glm::vec3& vec3); @@ -139,7 +174,9 @@ public slots: * @param {number} pitch - The pitch angle in degrees. * @param {number} yaw - The yaw angle in degrees. * @param {number} roll - The roll angle in degrees. - * @returns {Quat} A quaternion created using the pitch, yaw, and roll angles. + * @returns {Quat} A quaternion created using the pitch, yaw, and roll Euler angles. + * @example Create a rotation of 180 degrees about the y axis. + * var rotation = Quat.fromPitchYawRollDgrees(0, 180, 0 ); */ glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); @@ -149,20 +186,32 @@ public slots: * @param {number} pitch - The pitch angle in radians. * @param {number} yaw - The yaw angle in radians. * @param {number} roll - The roll angle in radians. - * @returns {Quat} A quaternion created from the pitch, yaw, and roll angles. + * @returns {Quat} A quaternion created from the pitch, yaw, and roll Euler angles. + * @example Create a rotation of 180 degrees about the y axis. + * var rotation = Quat.fromPitchYawRollRadians(0, Math.PI, 0); */ glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); /**jsdoc - * Calculate the inverse of a quaternion. Synonym for [Quat.conjugate]{@link Quat(0).conjugate}. + * Calculate the inverse of a quaternion. For a unit quaternion, its inverse is the same as its + * {@link Quat(0).conjugate|Quat.conjugate}. * @function Quat(0).inverse * @param {Quat} q - The quaternion. - * @returns {Quat} The inverse (conjugate) of q. + * @returns {Quat} The inverse of q. + * @example A quaternion multiplied by its inverse is a zero rotation. + * var quaternion = Quat.fromPitchYawRollDegrees(10, 20, 30); + * Quat.print("quaternion", quaternion, true); // dvec3(10.000000, 20.000004, 30.000004) + * var inverse = Quat.invserse(quaternion); + * Quat.print("inverse", inverse, true); // dvec3(1.116056, -22.242186, -28.451778) + * var identity = Quat.multiply(inverse, quaternion); + * Quat.print("identity", identity, true); // dvec3(0.000000, 0.000000, 0.000000) */ glm::quat inverse(const glm::quat& q); /**jsdoc - * Get the negative z-axis for the quaternion. This is a synonym for [Quat.getForward]{@link Quat(0).getForward}. + * Get the "front" direction that the camera would face if its orientation was set to the quaternion value. + * This is a synonym for {@link Quat(0).getForward|Quat.getForward}. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getFront * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -170,7 +219,9 @@ public slots: glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } /**jsdoc - * Get the negative z-axis for the quaternion. This is a synonym for [Quat.getFront]{@link Quat(0).getFront}. + * Get the "forward" direction that the camera would face if its orientation was set to the quaternion value. + * This is a synonym for {@link Quat(0).getFront|Quat.getFront}. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getForward * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -181,7 +232,8 @@ public slots: glm::vec3 getForward(const glm::quat& orientation); /**jsdoc - * Get the x-axis for the quaternion. + * Get the "right" direction that the camera would have if its orientation was set to the quaternion value. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getRight * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The x-axis rotated by orientation. @@ -189,7 +241,8 @@ public slots: glm::vec3 getRight(const glm::quat& orientation); /**jsdoc - * Get the y-axis for the quaternion. + * Get the "up" direction that the camera would have if its orientation was set to the quaternion value. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * @function Quat(0).getUp * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The y-axis rotated by orientation. @@ -198,10 +251,14 @@ public slots: /**jsdoc * Calculate the Euler angles for the quaternion, in degrees. (The "safe" in the name signifies that the angle results will - * not be garbage even when the rotation is particularly difficult to decompose.) + * not be garbage even when the rotation is particularly difficult to decompose with pitches around +/-90 degrees.) * @function Quat(0).safeEulerAngles * @param {Quat} orientation - A quaternion representing an orientation. - * @returns {Vec3} A {@link Vec3} of Euler angles for the orientation, in degrees. + * @returns {Vec3} A {@link Vec3} of Euler angles for the orientation, in degrees, the angles being the + * rotations about the x, y, and z axes. + * @example Report the camera yaw. + * var eulerAngles = Quat.safeEulerAngles(Camera.orientation); + * print("Camera yaw: " + eulerAngles.y); */ glm::vec3 safeEulerAngles(const glm::quat& orientation); @@ -209,10 +266,12 @@ public slots: * Generate a quaternion given an angle to rotate through and an axis to rotate about. * @function Quat(0).angleAxis * @param {number} angle - The angle to rotate through, in degrees. - * @param {Vec3} axis - The axis to rotate about. + * @param {Vec3} axis - The unit axis to rotate about. * @returns {Quat} A quaternion that is a rotation through angle degrees about the axis. - * WARNING: This value is in degrees whereas the value returned by [Quat.angle]{@link Quat(0).angleAxis} is + * WARNING: This value is in degrees whereas the value returned by {@link Quat(0).angle|Quat.angle} is * in radians. + * @example Calculate a rotation of 90 degrees about the direction your camera is looking. + * var rotation = Quat.angleAxis(90, Quat.getForward(Camera.orientation)); */ glm::quat angleAxis(float angle, const glm::vec3& v); @@ -221,6 +280,12 @@ public slots: * @function Quat(0).axis * @param {Quat} q - The quaternion. * @returns {Vec3} The normalized rotation axis for q. + * @example Get the rotation axis of a quaternion. + * var forward = Quat.getForward(Camera.orientation); + * var rotation = Quat.angleAxis(90, forward); + * var axis = Quat.axis(rotation); + * print("Forward: " + JSON.stringify(forward)); + * print("Axis: " + JSON.stringify(axis)); // Same value as forward. */ glm::vec3 axis(const glm::quat& orientation); @@ -228,8 +293,13 @@ public slots: * Get the rotation angle for a quaternion. * @function Quat(0).angle * @param {Quat} q - The quaternion. - * @returns {Vec3} The rotation angle for q, in radians. WARNING: This value is in radians - * whereas the value used by [Quat.angleAxis]{@link Quat(0).angleAxis} is in degrees. + * @returns {number} The rotation angle for q, in radians. WARNING: This value is in radians + * whereas the value used by {@link Quat(0).angleAxis|Quat.angleAxis} is in degrees. + * @example Get the rotation angle of a quaternion. + * var forward = Quat.getForward(Camera.orientation); + * var rotation = Quat.angleAxis(90, forward); + * var angle = Quat.angle(rotation); + * print("Angle: " + angle * 180 / Math.PI); // 90 degrees. */ float angle(const glm::quat& orientation); @@ -237,11 +307,14 @@ public slots: // alpha: 0.0 to 1.0? /**jsdoc * Compute a spherical linear interpolation between two rotations, safely handling two rotations that are very similar. - * See also, [Quat.slerp]{@link Quat(0).slerp}. + * See also, {@link Quat(0).slerp|Quat.slerp}. * @function Quat(0).mix * @param {Quat} q1 - The beginning rotation. * @param {Quat} q2 - The ending rotation. - * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. Specifies the proportion + * of q2's value to return in favor of q1's value. A value of 0.0 returns + * q1's value; 1.0 returns q2s's value. + * @returns {Quat} A spherical linear interpolation between rotations q1 and q2. * @example Animate between one rotation and another. * var dt = amountOfTimeThatHasPassed; * var mixFactor = amountOfTimeThatHasPassed / TIME_TO_COMPLETE; @@ -253,34 +326,51 @@ public slots: glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); /**jsdoc - * Compute a spherical linear interpolation between two rotations. - * See also, [Quat.mix]{@link Quat(0).mix}. + * Compute a spherical linear interpolation between two rotations, for rotations that are not very similar. + * See also, {@link Quat(0).mix|Quat.mix}. * @function Quat(0).slerp * @param {Quat} q1 - The beginning rotation. * @param {Quat} q2 - The ending rotation. - * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. Specifies the proportion + * of q2's value to return in favor of q1's value. A value of 0.0 returns + * q1's value; 1.0 returns q2s's value. + * @returns {Quat} A spherical linear interpolation between rotations q1 and q2. */ glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); /**jsdoc - * Compute a spherical quadratic interpolation between two rotations. + * Compute a spherical quadrangle interpolation between two rotations along a path oriented toward two other rotations. + * Equivalent to: Quat.slerp(Quat.slerp(q1, q2, alpha), Quat.slerp(s1, s2, alpha), 2 * alpha * (1.0 - alpha)). * @function Quat(0).squad - * @param {Quat} q1 - The rotation before to the beginning rotation. - * @param {Quat} q2 - The beginning rotation. - * @param {Quat} q3 - The ending rotation. - * @param {Quat} q4 - The rotation after the ending rotation. - * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. + * @param {Quat} q1 - Initial rotation. + * @param {Quat} q2 - Final rotation. + * @param {Quat} s1 - First control point. + * @param {Quat} s2 - Second control point. + * @param {number} alpha - The mixture coefficient between 0.0 and 1.0. A value of + * 0.0 returns q1's value; 1.0 returns q2s's value. + * @returns {Quat} A spherical quadrangle interpolation between rotations q1 and q2 using control + * points s1 and s2. */ glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); /**jsdoc - * Calculate the dot product of two quaternions. - * A zero value means the rotations are completely orthogonal to each other. The closer the rotations are to each other the - * more non-zero the value is (either positive or negative). Identical rotations have a dot product of +/- 1. + * Calculate the dot product of two quaternions. The closer the quaternions are to each other the more non-zero the value is + * (either positive or negative). Identical unit rotations have a dot product of +/- 1. * @function Quat(0).dot * @param {Quat} q1 - The first quaternion. * @param {Quat} q2 - The second quaternion. - * @returns {Quat} The dot product of q1 and q2. + * @returns {number} The dot product of q1 and q2. + * @example Testing unit quaternions for equality. + * var q1 = Quat.fromPitchYawRollDegrees(0, 0, 0); + * var q2 = Quat.fromPitchYawRollDegrees(0, 0, 0); + * print(Quat.equal(q1, q2)); // true + * var q3 = Quat.fromPitchYawRollDegrees(0, 0, 359.95); + * print(Quat.equal(q1, q3)); // false + * + * var dot = Quat.dot(q1, q3); + * print(dot); // -0.9999999403953552 + * var equal = Math.abs(1 - Math.abs(dot)) < 0.000001; + * print(equal); // true */ float dot(const glm::quat& q1, const glm::quat& q2); @@ -289,7 +379,8 @@ public slots: * @function Quat(0).print * @param {string} label - The label to print. * @param {Quat} q - The quaternion to print. - * @param {boolean} [asDegrees=false] - Whether to print the angles in degrees. + * @param {boolean} [asDegrees=false] - If true the angle values are printed in degrees, otherwise they are + * printed in radians. * @example Two ways of printing a label plus a quaternion's Euler angles. * var quaternion = Quat.fromPitchYawRollDegrees(0, 45, 0); * @@ -303,12 +394,12 @@ public slots: /**jsdoc * Test whether two quaternions are equal. Note: The quaternions must be exactly equal in order for - * true to be returned; it is often better to use [Quat.dot]{@link Quat(0).dot} and test for closeness to +/-1. + * true to be returned; it is often better to use {@link Quat(0).dot|Quat.dot} and test for closeness to +/-1. * @function Quat(0).equal * @param {Quat} q1 - The first quaternion. * @param {Quat} q2 - The second quaternion. * @returns {boolean} true if the quaternions are equal, otherwise false. - * @example Testing quaternions for equality. + * @example Testing unit quaternions for equality. * var q1 = Quat.fromPitchYawRollDegrees(0, 0, 0); * var q2 = Quat.fromPitchYawRollDegrees(0, 0, 0); * print(Quat.equal(q1, q2)); // true From f5b12a0a9d3febba6dc480c080c0d7753b28eb3e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Nov 2017 19:16:51 +1300 Subject: [PATCH 079/137] Fix typo --- libraries/script-engine/src/Quat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index a0c1378b23..23e8e3b896 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -318,7 +318,7 @@ public slots: * @example Animate between one rotation and another. * var dt = amountOfTimeThatHasPassed; * var mixFactor = amountOfTimeThatHasPassed / TIME_TO_COMPLETE; - * if (mixFactor) > 1) { + * if (mixFactor > 1) { * mixFactor = 1; * } * var newRotation = Quat.mix(startRotation, endRotation, mixFactor); From 9bc165d8b40e4853e237fdb5aabd625ab466231f Mon Sep 17 00:00:00 2001 From: Daniela Fontes Date: Thu, 30 Nov 2017 18:49:53 +0000 Subject: [PATCH 080/137] Changing to Smaller Avatar Resizes Tablet --- interface/src/avatar/MyAvatar.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index df2089223b..28ecb07d31 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -669,6 +669,12 @@ void MyAvatar::updateSensorToWorldMatrix() { glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); + bool hasSensorToWorldScaleChanged = false; + + if (abs(AvatarData::getSensorToWorldScale() - sensorToWorldScale) > 0.001f) { + hasSensorToWorldScaleChanged = true; + } + lateUpdatePalms(); if (_enableDebugDrawSensorToWorldMatrix) { @@ -677,9 +683,15 @@ void MyAvatar::updateSensorToWorldMatrix() { } _sensorToWorldMatrixCache.set(_sensorToWorldMatrix); - + updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache); updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); + + if (hasSensorToWorldScaleChanged) { + emit sensorToWorldScaleChanged(sensorToWorldScale); + //qDebug() << "Debug: emit sensorToWorldScaleChanged " << sensorToWorldScale; + } + } // Update avatar head rotation with sensor data @@ -1404,6 +1416,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); _headBoneSet.clear(); emit skeletonChanged(); + } @@ -1439,6 +1452,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN UserActivityLogger::getInstance().changedModel("skeleton", urlString); } markIdentityDataChanged(); + } void MyAvatar::setAttachmentData(const QVector& attachmentData) { From c0ca7a129d2cff060ad73b7e8ed367fa28159dc1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 5 Dec 2017 17:33:11 +0100 Subject: [PATCH 081/137] Fixed issue with far distance of cascades being underestimated, especially first cascade --- libraries/render-utils/src/LightStage.cpp | 68 +++++++++---- libraries/render-utils/src/LightStage.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 22 +---- libraries/shared/src/ViewFrustum.cpp | 97 ++++++++++++++++++- libraries/shared/src/ViewFrustum.h | 14 ++- 5 files changed, 168 insertions(+), 39 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 5562e0fa2d..28a54cefca 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -53,6 +53,41 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { return _frustum->getProjection(); } +float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, + float left, float right, float bottom, float top, float viewMaxShadowDistance) const { + // Far distance should be extended to the intersection of the infinitely extruded shadow frustum + // with the view frustum side planes. To do so, we generate 10 triangles in shadow space which are the result of + // tesselating the side and far faces of the view frustum and clip them with the 4 side planes of the + // shadow frustum. The resulting clipped triangle vertices with the farthest Z gives the desired + // shadow frustum far distance. + std::array viewFrustumTriangles; + Plane shadowClipPlanes[4] = { + Plane(glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, top, 0.0f)), + Plane(glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, bottom, 0.0f)), + Plane(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(left, 0.0f, 0.0f)), + Plane(glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(right, 0.0f, 0.0f)) + }; + + viewFrustum.tesselateSidesAndFar(shadowViewInverse, viewFrustumTriangles.data(), viewMaxShadowDistance); + + static const int MAX_TRIANGLE_COUNT = 16; + auto far = 0.0f; + + for (auto& triangle : viewFrustumTriangles) { + Triangle clippedTriangles[MAX_TRIANGLE_COUNT]; + auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT); + + for (auto i = 0; i < clippedTriangleCount; i++) { + const auto& clippedTriangle = clippedTriangles[i]; + far = glm::max(far, -clippedTriangle.v0.z); + far = glm::max(far, -clippedTriangle.v1.z); + far = glm::max(far, -clippedTriangle.v2.z); + } + } + + return far; +} + LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; @@ -62,11 +97,12 @@ LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, + float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { - assert(viewMinShadowDistance < viewMaxShadowDistance); + assert(viewMinCascadeShadowDistance < viewMaxCascadeShadowDistance); assert(nearDepth < farDepth); - assert(viewOverlapDistance > 0.0f); + assert(viewCascadeOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -89,17 +125,17 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Position the keylight frustum cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); - const Transform view{ cascade._frustum->getView()}; - const Transform viewInverse{ view.getInverseMatrix() }; + const Transform shadowView{ cascade._frustum->getView()}; + const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; - auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance); - auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance); + auto nearCorners = viewFrustum.getCorners(viewMinCascadeShadowDistance); + auto farCorners = viewFrustum.getCorners(viewMaxCascadeShadowDistance); - vec3 min{ viewInverse.transform(nearCorners.bottomLeft) }; + vec3 min{ shadowViewInverse.transform(nearCorners.bottomLeft) }; vec3 max{ min }; // Expand keylight frustum to fit view frustum - auto fitFrustum = [&min, &max, &viewInverse](const vec3& viewCorner) { - const auto corner = viewInverse.transform(viewCorner); + auto fitFrustum = [&min, &max, &shadowViewInverse](const vec3& viewCorner) { + const auto corner = shadowViewInverse.transform(viewCorner); min.x = glm::min(min.x, corner.x); min.y = glm::min(min.y, corner.y); @@ -116,13 +152,11 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.bottomRight); fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); - - // TODO: Far distance should be extended to the intersection of the exteruded shadow frustum far plane - // with the view frustum. // Re-adjust near shadow distance auto near = glm::min(-max.z, nearDepth); - auto far = -min.z; + auto far = cascade.computeFarDistance(viewFrustum, shadowViewInverse, min.x, max.x, min.y, max.y, viewMaxShadowDistance); + glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); cascade._frustum->setProjection(ortho); @@ -132,10 +166,10 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); if (cascadeIndex == getCascadeCount() - 1) { - schema.maxDistance = viewMaxShadowDistance; - schema.invFalloffDistance = 1.0f / viewOverlapDistance; + schema.maxDistance = viewMaxCascadeShadowDistance; + schema.invFalloffDistance = 1.0f / viewCascadeOverlapDistance; } - schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 0a0a67f591..7cf0961e3a 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -65,12 +65,16 @@ public: private: std::shared_ptr _frustum; + + float computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, + float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; Shadow(model::LightPointer light, unsigned int cascadeCount = 1); void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, + float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 6b00a14d23..c5ed26823a 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -89,24 +89,7 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum for (i = 0; i < 8; i++) { sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast(i))); } - // This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum - // changes order especially as we don't need to test the NEAR and FAR planes. - static const ViewFrustum::PlaneIndex planeIndices[4] = { - ViewFrustum::TOP_PLANE, - ViewFrustum::BOTTOM_PLANE, - ViewFrustum::LEFT_PLANE, - ViewFrustum::RIGHT_PLANE - }; - // Same goes for the shadow frustum planes. - for (i = 0; i < 4; i++) { - const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]]; - // We assume the transform doesn't have a non uniform scale component to apply the - // transform to the normal without using the correct transpose of inverse, which should be the - // case for a view matrix. - auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal()); - auto planePoint = shadowViewInverse.transform(worldPlane.getPoint()); - shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint); - } + shadowFrustum.getUniformlyTransformedSidePlanes(shadowViewInverse, shadowClipPlanes); float near = std::numeric_limits::max(); float far = 0.0f; @@ -278,7 +261,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O minCascadeDistance = std::max(minCascadeDistance, nearClip); } maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, + shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index dcbfd83ec7..37c01510f3 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -691,7 +691,7 @@ void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furth } } -const ViewFrustum::Corners ViewFrustum::getCorners(const float& depth) const { +const ViewFrustum::Corners ViewFrustum::getCorners(const float depth) const { glm::vec3 normal = glm::normalize(_direction); auto getCorner = [&](enum::BoxVertex nearCorner, enum::BoxVertex farCorner) { @@ -750,3 +750,98 @@ void ViewFrustum::invalidate() { } _centerSphereRadius = -1.0e6f; // -10^6 should be negative enough } + +void ViewFrustum::getSidePlanes(::Plane planes[4]) const { + planes[0] = _planes[TOP_PLANE]; + planes[1] = _planes[BOTTOM_PLANE]; + planes[2] = _planes[LEFT_PLANE]; + planes[3] = _planes[RIGHT_PLANE]; +} + +void ViewFrustum::getTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const { + glm::mat4 normalTransform; + transform.getInverseTransposeMatrix(normalTransform); + getSidePlanes(planes); + for (auto i = 0; i < 4; i++) { + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse. + auto transformedNormal = normalTransform * Transform::Vec4(planes[i].getNormal(), 0.0f); + auto planePoint = transform.transform(planes[i].getPoint()); + glm::vec3 planeNormal(transformedNormal.x, transformedNormal.y, transformedNormal.z); + planes[i].setNormalAndPoint(planeNormal, planePoint); + } +} + +void ViewFrustum::getUniformlyTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const { + getSidePlanes(planes); + for (auto i = 0; i < 4; i++) { + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse. + auto planeNormal = transform.transformDirection(planes[i].getNormal()); + auto planePoint = transform.transform(planes[i].getPoint()); + planes[i].setNormalAndPoint(planeNormal, planePoint); + } +} + +void ViewFrustum::tesselateSides(Triangle triangles[8]) const { + tesselateSides(_cornersWorld, triangles); +} + +void ViewFrustum::tesselateSides(const Transform& transform, Triangle triangles[8]) const { + glm::vec3 points[8]; + + for (auto i = 0; i < 8; i++) { + points[i] = transform.transform(_cornersWorld[i]); + } + + tesselateSides(points, triangles); +} + +void ViewFrustum::tesselateSidesAndFar(const Transform& transform, Triangle triangles[10], float farDistance) const { + glm::vec3 points[8]; + + // First 4 points are at near + for (auto i = 0; i < 4; i++) { + points[i] = transform.transform(_cornersWorld[i]); + } + auto farCorners = getCorners(farDistance); + + points[BOTTOM_LEFT_FAR] = transform.transform(farCorners.bottomLeft); + points[BOTTOM_RIGHT_FAR] = transform.transform(farCorners.bottomRight); + points[TOP_LEFT_FAR] = transform.transform(farCorners.topLeft); + points[TOP_RIGHT_FAR] = transform.transform(farCorners.topRight); + + tesselateSides(points, triangles); + // Add far side + triangles[8].v0 = points[BOTTOM_LEFT_FAR]; + triangles[8].v1 = points[BOTTOM_RIGHT_FAR]; + triangles[8].v2 = points[TOP_RIGHT_FAR]; + + triangles[9].v0 = points[BOTTOM_LEFT_FAR]; + triangles[9].v1 = points[TOP_LEFT_FAR]; + triangles[9].v2 = points[TOP_RIGHT_FAR]; +} + +void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8]) { + static_assert(BOTTOM_RIGHT_NEAR == (BOTTOM_LEFT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_RIGHT_NEAR == (BOTTOM_RIGHT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_LEFT_NEAR == (TOP_RIGHT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(BOTTOM_RIGHT_FAR == (BOTTOM_LEFT_FAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_RIGHT_FAR == (BOTTOM_RIGHT_FAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_LEFT_FAR == (TOP_RIGHT_FAR + 1), "Assuming a certain sequence in corners"); + static const int triangleVertexIndices[8][3] = { + { BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR }, + { BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR },{ BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR }, + { TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_FAR },{ TOP_LEFT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, + { BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR } + }; + + for (auto i = 0; i < 8; i++) { + auto& triangle = triangles[i]; + auto vertexIndices = triangleVertexIndices[i]; + + triangle.v0 = points[vertexIndices[0]]; + triangle.v1 = points[vertexIndices[1]]; + triangle.v2 = points[vertexIndices[2]]; + } +} diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 98f666d666..d711e2a80a 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -74,7 +74,7 @@ public: glm::vec3 bottomRight; // Get the corners depth units from frustum position, along frustum orientation }; - const Corners getCorners(const float& depth) const; + const Corners getCorners(const float depth) const; // getters for corners const glm::vec3& getFarTopLeft() const { return _cornersWorld[TOP_LEFT_FAR]; } @@ -90,6 +90,10 @@ public: void setCenterRadius(float radius) { _centerSphereRadius = radius; } float getCenterRadius() const { return _centerSphereRadius; } + void tesselateSides(Triangle triangles[8]) const; + void tesselateSides(const Transform& transform, Triangle triangles[8]) const; + void tesselateSidesAndFar(const Transform& transform, Triangle triangles[10], float farDistance) const; + void calculate(); typedef enum { OUTSIDE = 0, INTERSECT, INSIDE } intersection; @@ -134,6 +138,12 @@ public: enum PlaneIndex { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE, NUM_PLANES }; const ::Plane* getPlanes() const { return _planes; } + void getSidePlanes(::Plane planes[4]) const; + // Transform can have a different scale value in X,Y,Z components + void getTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const; + // Transform is assumed to have the same scale value in all three X,Y,Z components, which + // allows for a faster computation. + void getUniformlyTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const; void invalidate(); // causes all reasonable intersection tests to fail @@ -175,6 +185,8 @@ private: template CubeProjectedPolygon computeProjectedPolygon(const TBOX& box) const; + static void tesselateSides(const glm::vec3 points[8], Triangle triangles[8]); + }; using ViewFrustumPointer = std::shared_ptr; From e16b427ab698da2b68d8c80f5965f19efeac8833 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 5 Dec 2017 18:57:04 +0100 Subject: [PATCH 082/137] Switched to 4 cascades for key light and working on a better distribution --- libraries/render-utils/src/LightStage.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 28a54cefca..59ad63890a 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,7 +23,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c5ed26823a..24757496c8 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -240,26 +240,33 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto nearClip = args->getViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); - static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; + // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? + static const auto HIGH_CASCADE_MAX_DISTANCE = 40.0f; + static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f; + // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a + // tighter distribution around the view position. + static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; + float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; float minCascadeDistance = nearClip; float shadowOverlapDistance = 0.0f; if (globalShadow->getCascadeCount() > 1) { - static const float LOW_CASCADE_MAX_DISTANCE = 3.0f; - const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1)); + const auto deltaCascadeMaxDistance = (HIGH_CASCADE_MAX_DISTANCE - LOW_CASCADE_MAX_DISTANCE); + const auto maxAlpha = powf(_cascadeIndex / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); + const auto minAlpha = powf(std::max(_cascadeIndex-1, 0) / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex); - minCascadeDistance = maxCascadeDistance / cascadeLevelScale; + maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; + minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 4.0f; - maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearClip; } else { minCascadeDistance = std::max(minCascadeDistance, nearClip); } + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + maxCascadeDistance += shadowOverlapDistance; maxCascadeDistance = std::min(maxCascadeDistance, farClip); globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); From 1d8d8335c5b27844f8007b61ef95f2ac8c01ef62 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 10:05:39 +0100 Subject: [PATCH 083/137] Moved shadow cascade distances computation to shadow, not shadow task --- .../src/RenderableZoneEntityItem.cpp | 4 +- libraries/render-utils/src/LightStage.cpp | 72 +++++++++++++++---- libraries/render-utils/src/LightStage.h | 19 +++-- .../render-utils/src/RenderShadowTask.cpp | 34 +-------- 4 files changed, 76 insertions(+), 53 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index d9a5382f16..04da70d733 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -27,6 +27,8 @@ // Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; +static const unsigned int SUN_SHADOW_CASCADE_COUNT{ 4 }; +static const float SUN_SHADOW_MAX_DISTANCE{ 40.0f }; using namespace render; using namespace render::entities; @@ -116,7 +118,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { // Do we need to allocate the light in the stage ? if (LightStage::isIndexInvalid(_sunIndex)) { _sunIndex = _stage->addLight(_sunLight); - _shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT); + _shadowIndex = _stage->addShadow(_sunIndex, SUN_SHADOW_MAX_DISTANCE, SUN_SHADOW_CASCADE_COUNT); } else { _stage->updateLightArrayBuffer(_sunIndex); } diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 59ad63890a..a0cb5b3f2a 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,7 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -40,7 +39,10 @@ LightStage::Shadow::Schema::Schema() { maxDistance = 20.0f; } -LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { +LightStage::Shadow::Cascade::Cascade() : + _frustum{ std::make_shared() }, + _minDistance{ 0.0f }, + _maxDistance{ 20.0f } { framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); map = framebuffer->getDepthStencilBuffer(); } @@ -88,21 +90,66 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru return far; } -LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { +LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount) : + _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; schema.cascadeCount = cascadeCount; _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); _cascades.resize(cascadeCount); + + setMaxDistance(maxDistance); +} + +void LightStage::Shadow::setMaxDistance(float value) { + _maxDistance = std::max(0.0f, value); + + // Distribute the cascades along that distance + // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? + static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f; + // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a + // tighter distribution around the view position. + static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; + + if (_cascades.size() == 1) { + _cascades.front().setMinDistance(0.0f); + _cascades.front().setMaxDistance(_maxDistance); + } else { + for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float maxCascadeDistance; + float minCascadeDistance; + float shadowOverlapDistance; + + const auto deltaCascadeMaxDistance = (_maxDistance - LOW_CASCADE_MAX_DISTANCE); + const auto maxAlpha = powf(cascadeIndex / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); + const auto minAlpha = powf(std::max(cascadeIndex - 1, 0) / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); + + maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; + minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; + + if (cascadeIndex == 0) { + minCascadeDistance = 0.0f; + } else { + minCascadeDistance = std::max(minCascadeDistance, 0.0f); + } + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + maxCascadeDistance += shadowOverlapDistance; + + _cascades[cascadeIndex].setMinDistance(minCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance); + } + } + + // Update the buffer + const auto& lastCascade = _cascades.back(); + auto& schema = _schemaBuffer.edit(); + schema.maxDistance = _maxDistance; + schema.invFalloffDistance = 3.0f / (lastCascade.getMaxDistance() - lastCascade.getMinDistance()); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, - float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { - assert(viewMinCascadeShadowDistance < viewMaxCascadeShadowDistance); assert(nearDepth < farDepth); - assert(viewCascadeOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -119,6 +166,9 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie } auto& cascade = _cascades[cascadeIndex]; + const auto viewMinCascadeShadowDistance = std::max(viewFrustum.getNearClip(), cascade.getMinDistance()); + const auto viewMaxCascadeShadowDistance = std::min(viewFrustum.getFarClip(), cascade.getMaxDistance()); + const auto viewMaxShadowDistance = _cascades.back().getMaxDistance(); cascade._frustum->setOrientation(orientation); @@ -165,10 +215,6 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); - if (cascadeIndex == getCascadeCount() - 1) { - schema.maxDistance = viewMaxCascadeShadowDistance; - schema.invFalloffDistance = 1.0f / viewCascadeOverlapDistance; - } schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); } @@ -218,12 +264,12 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { } } -LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) { +LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) { auto light = getLight(lightIndex); Index shadowId = INVALID_INDEX; if (light) { assert(_descs[lightIndex].shadowId == INVALID_INDEX); - shadowId = _shadows.newElement(std::make_shared(light, cascadeCount)); + shadowId = _shadows.newElement(std::make_shared(light, maxDistance, cascadeCount)); _descs[lightIndex].shadowId = shadowId; } return shadowId; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 7cf0961e3a..ed9d330934 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -31,8 +31,6 @@ public: static std::string _stageName; static const std::string& getName() { return _stageName; } - static const unsigned int SUN_SHADOW_CASCADE_COUNT; - using Index = render::indexed_container::Index; static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } @@ -62,19 +60,24 @@ public: const glm::mat4& getView() const; const glm::mat4& getProjection() const; + void setMinDistance(float value) { _minDistance = value; } + void setMaxDistance(float value) { _maxDistance = value; } + float getMinDistance() const { return _minDistance; } + float getMaxDistance() const { return _maxDistance; } + private: std::shared_ptr _frustum; + float _minDistance; + float _maxDistance; float computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; - Shadow(model::LightPointer light, unsigned int cascadeCount = 1); + Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, - float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); @@ -83,6 +86,9 @@ public: unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); } const Cascade& getCascade(unsigned int index) const { return _cascades[index]; } + float getMaxDistance() const { return _maxDistance; } + void setMaxDistance(float value); + const model::LightPointer& getLight() const { return _light; } protected: @@ -94,6 +100,7 @@ public: static const glm::mat4 _biasMatrix; model::LightPointer _light; + float _maxDistance; Cascades _cascades; class Schema : public ShadowParameters { @@ -111,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U); + Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 24757496c8..a0d691b10e 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -237,39 +237,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto nearClip = args->getViewFrustum().getNearClip(); - const auto farClip = args->getViewFrustum().getFarClip(); - - // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto HIGH_CASCADE_MAX_DISTANCE = 40.0f; - static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f; - // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a - // tighter distribution around the view position. - static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; - - float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; - float minCascadeDistance = nearClip; - float shadowOverlapDistance = 0.0f; - - if (globalShadow->getCascadeCount() > 1) { - const auto deltaCascadeMaxDistance = (HIGH_CASCADE_MAX_DISTANCE - LOW_CASCADE_MAX_DISTANCE); - const auto maxAlpha = powf(_cascadeIndex / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - const auto minAlpha = powf(std::max(_cascadeIndex-1, 0) / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - - maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; - minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; - } - - if (_cascadeIndex == 0) { - minCascadeDistance = nearClip; - } else { - minCascadeDistance = std::max(minCascadeDistance, nearClip); - } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; - maxCascadeDistance += shadowOverlapDistance; - maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, - shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); From f9641afcd0e111ac4a10861e0d726bc2fa545101 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 10:48:04 +0100 Subject: [PATCH 084/137] Modified cascade distance distribution to blend between a desired resolution and an automatic logarithm distribution --- libraries/render-utils/src/LightStage.cpp | 55 +++++++++++++---------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index a0cb5b3f2a..54154da2ea 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -102,41 +102,50 @@ LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigne } void LightStage::Shadow::setMaxDistance(float value) { - _maxDistance = std::max(0.0f, value); + static const auto OVERLAP_FACTOR = 1.0f / 4.0f; - // Distribute the cascades along that distance - // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f; - // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a - // tighter distribution around the view position. - static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; + _maxDistance = std::max(0.0f, value); if (_cascades.size() == 1) { _cascades.front().setMinDistance(0.0f); _cascades.front().setMaxDistance(_maxDistance); } else { + // Distribute the cascades along that distance + // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? + static const auto LOW_MAX_DISTANCE = 2.0f; + static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions + + // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain + // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow + // and an optimal own based on the global min and max shadow distance, all cascades considered. The final + // distance is a gradual blend between the two + const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); + const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); + + float maxCascadeUserDistance = LOW_MAX_DISTANCE; + float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; + float minCascadeDistance = 0.0f; + for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float blendFactor = cascadeIndex / float(_cascades.size() - 1); float maxCascadeDistance; - float minCascadeDistance; - float shadowOverlapDistance; - const auto deltaCascadeMaxDistance = (_maxDistance - LOW_CASCADE_MAX_DISTANCE); - const auto maxAlpha = powf(cascadeIndex / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - const auto minAlpha = powf(std::max(cascadeIndex - 1, 0) / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - - maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; - minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; - - if (cascadeIndex == 0) { - minCascadeDistance = 0.0f; + if (cascadeIndex == _cascades.size() - 1) { + maxCascadeDistance = _maxDistance; } else { - minCascadeDistance = std::max(minCascadeDistance, 0.0f); + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; - maxCascadeDistance += shadowOverlapDistance; + + float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) * OVERLAP_FACTOR; _cascades[cascadeIndex].setMinDistance(minCascadeDistance); - _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); + + // Compute distances for next cascade + minCascadeDistance = maxCascadeDistance; + maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; + maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; + maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } } @@ -144,7 +153,7 @@ void LightStage::Shadow::setMaxDistance(float value) { const auto& lastCascade = _cascades.back(); auto& schema = _schemaBuffer.edit(); schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 3.0f / (lastCascade.getMaxDistance() - lastCascade.getMinDistance()); + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*(lastCascade.getMaxDistance() - lastCascade.getMinDistance())); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, From ff39558d27ed22e5885b3b68b7bf927e5fe94226 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 11:22:15 +0100 Subject: [PATCH 085/137] Automatic shadow bias computation from cascade texel density --- libraries/render-utils/src/LightStage.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 54154da2ea..4ac5cb058d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,6 +23,8 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; +static const auto MAX_BIAS = 0.006f; + const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -30,7 +32,7 @@ LightStage::LightStage() { LightStage::Shadow::Schema::Schema() { ShadowTransform defaultTransform; - defaultTransform.bias = 0.005f; + defaultTransform.bias = MAX_BIAS; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; @@ -225,6 +227,11 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); + // Adapt shadow bias to shadow resolution with a totally empirical formula + const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); + const auto REFERENCE_TEXEL_DENSITY = 20.0f; + const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; + schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { From 89b1ef2e1901508a283a423bf589acdba87287da Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 12:07:30 +0100 Subject: [PATCH 086/137] Tweaked bias once more --- libraries/render-utils/src/LightStage.cpp | 6 +++--- libraries/render-utils/src/LightStage.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 4ac5cb058d..5a640e1105 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -114,7 +114,7 @@ void LightStage::Shadow::setMaxDistance(float value) { } else { // Distribute the cascades along that distance // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto LOW_MAX_DISTANCE = 2.0f; + static const auto LOW_MAX_DISTANCE = 1.5f; static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain @@ -214,7 +214,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); - // Re-adjust near shadow distance + // Re-adjust near and far shadow distance auto near = glm::min(-max.z, nearDepth); auto far = cascade.computeFarDistance(viewFrustum, shadowViewInverse, min.x, max.x, min.y, max.y, viewMaxShadowDistance); @@ -229,7 +229,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 20.0f; + const auto REFERENCE_TEXEL_DENSITY = 10.0f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ed9d330934..f632354dd1 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -118,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); + Index addShadow(Index lightIndex, float maxDistance = 16.0f, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); From c9c93370daf6b48384431fe087dba96b8079775f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 15:26:44 +0100 Subject: [PATCH 087/137] Fixed weird bug with objects suddenly poping out of the shadow map. Was due to objects with own pipeline corrupting the render state. Don't know why though --- libraries/render-utils/src/LightStage.cpp | 4 ++-- libraries/render-utils/src/LightStage.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 18 ++++++++++++++++-- libraries/render-utils/src/RenderViewTask.cpp | 14 ++++++++++---- libraries/shared/src/ViewFrustum.cpp | 2 ++ 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 5a640e1105..89dc729185 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -119,7 +119,7 @@ void LightStage::Shadow::setMaxDistance(float value) { // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow - // and an optimal own based on the global min and max shadow distance, all cascades considered. The final + // and an optimal one based on the global min and max shadow distance, all cascades considered. The final // distance is a gradual blend between the two const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); @@ -229,7 +229,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 10.0f; + const auto REFERENCE_TEXEL_DENSITY = 12.0f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index f632354dd1..ed9d330934 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -118,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, float maxDistance = 16.0f, unsigned int cascadeCount = 1U); + Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index a0d691b10e..d362c14df8 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -22,6 +22,8 @@ #include "DeferredLightingEffect.h" #include "FramebufferCache.h" +#include "RenderUtilsLogging.h" + // These values are used for culling the objects rendered in the shadow map // but are readjusted afterwards #define SHADOW_FRUSTUM_NEAR 1.0f @@ -123,7 +125,8 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con return; } - const auto& fbo = shadow->getCascade(_cascadeIndex).framebuffer; + auto& cascade = shadow->getCascade(_cascadeIndex); + auto& fbo = cascade.framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -162,6 +165,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); std::vector skinnedShapeKeys{}; + std::vector ownPipelineShapeKeys{}; // Iterate through all inShapes and render the unskinned args->_shapePipeline = shadowPipeline; @@ -169,8 +173,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con for (auto items : inShapes) { if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); - } else { + } else if (!items.first.hasOwnPipeline()) { renderItems(renderContext, items.second); + } else { + ownPipelineShapeKeys.push_back(items.first); } } @@ -181,7 +187,15 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con renderItems(renderContext, inShapes.at(key)); } + // Finally render the items with their own pipeline last to prevent them from breaking the + // render state. This is probably a temporary code as there is probably something better + // to do in the render call of objects that have their own pipeline. args->_shapePipeline = nullptr; + for (const auto& key : ownPipelineShapeKeys) { + args->_itemShapeKey = key._flags.to_ulong(); + renderItems(renderContext, inShapes.at(key)); + } + args->_batch = nullptr; }); } diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 1085a1148c..dc6c66e058 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,15 +14,21 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" - - void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights // but the cullFunctor passed is probably tailored for perspective projection and culls too much. - // TODO : create a special cull functor for this. - task.addJob("RenderShadowTask", nullptr); + task.addJob("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) { + // Cull only objects that are too small relatively to shadow frustum + auto& frustum = args->getViewFrustum(); + auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth()); + const auto boundsRadius = bounds.getDimensions().length(); + const auto relativeBoundRadius = boundsRadius / frustumSize; + const auto threshold = 1e-3f; + return relativeBoundRadius > threshold; + return true; + }); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 37c01510f3..5b016d4e91 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -71,6 +71,8 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); top /= top.w; _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); + _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; + _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; } // ViewFrustum::calculate() From 014e81b2f479955eed71979af529622ab2335cc9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 17:06:48 +0100 Subject: [PATCH 088/137] Added normal based adaptive depth bias --- libraries/render-utils/src/LightStage.cpp | 34 +++++++++++-------- libraries/render-utils/src/Shadow.slh | 17 ++++++---- libraries/render-utils/src/ShadowCore.slh | 7 ++-- libraries/render-utils/src/Shadows_shared.slh | 3 +- .../src/directional_ambient_light_shadow.slf | 2 +- .../src/directional_skybox_light_shadow.slf | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 89dc729185..b49408a1f4 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -36,7 +36,7 @@ LightStage::Shadow::Schema::Schema() { std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; - invCascadeBlendWidth = 1.0f / 0.1f; + invCascadeBlendWidth = 1.0f / 0.2f; invFalloffDistance = 1.0f / 2.0f; maxDistance = 20.0f; } @@ -104,7 +104,10 @@ LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigne } void LightStage::Shadow::setMaxDistance(float value) { - static const auto OVERLAP_FACTOR = 1.0f / 4.0f; + // This overlaping factor isn't really used directly for blending of shadow cascades. It + // just there to be sure the cascades do overlap. The blending width used is relative + // to the UV space and is set in the Schema with invCascadeBlendWidth. + static const auto OVERLAP_FACTOR = 1.0f / 5.0f; _maxDistance = std::max(0.0f, value); @@ -114,7 +117,7 @@ void LightStage::Shadow::setMaxDistance(float value) { } else { // Distribute the cascades along that distance // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto LOW_MAX_DISTANCE = 1.5f; + static const auto LOW_MAX_DISTANCE = 2.0f; static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain @@ -135,10 +138,10 @@ void LightStage::Shadow::setMaxDistance(float value) { if (cascadeIndex == _cascades.size() - 1) { maxCascadeDistance = _maxDistance; } else { - maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor; + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; } - float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) * OVERLAP_FACTOR; + float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; _cascades[cascadeIndex].setMinDistance(minCascadeDistance); _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); @@ -155,7 +158,7 @@ void LightStage::Shadow::setMaxDistance(float value) { const auto& lastCascade = _cascades.back(); auto& schema = _schemaBuffer.edit(); schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*(lastCascade.getMaxDistance() - lastCascade.getMinDistance())); + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, @@ -164,16 +167,16 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum - const auto& direction = glm::normalize(_light->getDirection()); + const auto& lightDirection = glm::normalize(_light->getDirection()); glm::quat orientation; - if (direction == IDENTITY_UP) { + if (lightDirection == IDENTITY_UP) { orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); - } else if (direction == -IDENTITY_UP) { + } else if (lightDirection == -IDENTITY_UP) { orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FORWARD, IDENTITY_UP)); } else { - auto side = glm::normalize(glm::cross(direction, IDENTITY_UP)); - auto up = glm::normalize(glm::cross(side, direction)); - orientation = glm::quat_cast(glm::mat3(side, up, -direction)); + auto side = glm::normalize(glm::cross(lightDirection, IDENTITY_UP)); + auto up = glm::normalize(glm::cross(side, lightDirection)); + orientation = glm::quat_cast(glm::mat3(side, up, -lightDirection)); } auto& cascade = _cascades[cascadeIndex]; @@ -184,7 +187,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->setOrientation(orientation); // Position the keylight frustum - cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); + cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection); const Transform shadowView{ cascade._frustum->getView()}; const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; @@ -229,9 +232,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 12.0f; + const auto REFERENCE_TEXEL_DENSITY = 7.5f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); + // TODO: this isn't the best place to do this as this is common for all cascades and this is called for + // each cascade at each frame. + schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index c4b7828688..a12dd0f4a4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -71,8 +71,8 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { return offsets; } -float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { - +float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) { + shadowTexcoord.z -= bias; float shadowAttenuation = 0.25 * ( fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) + @@ -83,24 +83,27 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { +float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); + // Multiply bias if we are at a grazing angle with light + float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal)); + float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor); + return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } -float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { +float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]); } float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); // Falloff to max distance diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 0a34cafe6f..9b3b086598 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -41,11 +41,14 @@ float getShadowBias(int cascadeIndex) { return shadow.cascades[cascadeIndex].bias; } +vec3 getShadowDirInViewSpace() { + return shadow.lightDirInViewSpace; +} + // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - float bias = getShadowBias(cascadeIndex); - return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); + return vec4(shadowCoord.xyz, 1.0); } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 2797f4e962..abb226421c 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -4,7 +4,7 @@ # define VEC3 glm::vec3 #else # define MAT4 mat4 -# define VEC3 ve3 +# define VEC3 vec3 #endif #define SHADOW_CASCADE_MAX_COUNT 4 @@ -20,6 +20,7 @@ struct ShadowTransform { struct ShadowParameters { ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; + VEC3 lightDirInViewSpace; int cascadeCount; float invMapSize; float invCascadeBlendWidth; diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index b791c9297a..f7ea8c5966 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); if (frag.mode == FRAG_MODE_UNLIT) { discard; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow.slf b/libraries/render-utils/src/directional_skybox_light_shadow.slf index 2bccc68550..37c9ae7fba 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { From 8d710c8f73abb3587cd31002288e59165b1ae0a2 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 18:08:27 +0100 Subject: [PATCH 089/137] Factorised out some computation needed for all cascades --- libraries/render-utils/src/LightStage.cpp | 32 +++++++++++-------- libraries/render-utils/src/LightStage.h | 6 ++-- .../render-utils/src/RenderShadowTask.cpp | 28 ++++++++++++---- libraries/render-utils/src/RenderShadowTask.h | 21 ++++++++---- 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index b49408a1f4..95629b5c3d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -161,13 +161,11 @@ void LightStage::Shadow::setMaxDistance(float value) { schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); } -void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, +void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) { assert(nearDepth < farDepth); - assert(cascadeIndex < _cascades.size()); - // Orient the keylight frustum - const auto& lightDirection = glm::normalize(_light->getDirection()); + auto lightDirection = glm::normalize(_light->getDirection()); glm::quat orientation; if (lightDirection == IDENTITY_UP) { orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); @@ -179,16 +177,27 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie orientation = glm::quat_cast(glm::mat3(side, up, -lightDirection)); } + // Position the keylight frustum + auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection; + for (auto& cascade : _cascades) { + cascade._frustum->setOrientation(orientation); + cascade._frustum->setPosition(position); + } + // Update the buffer + auto& schema = _schemaBuffer.edit(); + schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); +} + +void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float nearDepth, float farDepth) { + assert(nearDepth < farDepth); + assert(cascadeIndex < _cascades.size()); + auto& cascade = _cascades[cascadeIndex]; const auto viewMinCascadeShadowDistance = std::max(viewFrustum.getNearClip(), cascade.getMinDistance()); const auto viewMaxCascadeShadowDistance = std::min(viewFrustum.getFarClip(), cascade.getMaxDistance()); const auto viewMaxShadowDistance = _cascades.back().getMaxDistance(); - cascade._frustum->setOrientation(orientation); - - // Position the keylight frustum - cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection); - const Transform shadowView{ cascade._frustum->getView()}; const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; @@ -235,12 +244,9 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie const auto REFERENCE_TEXEL_DENSITY = 7.5f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); - // TODO: this isn't the best place to do this as this is common for all cascades and this is called for - // each cascade at each frame. - schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); } -void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { +void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { assert(cascadeIndex < _cascades.size()); const Transform view{ shadowFrustum.getView() }; const Transform viewInverse{ view.getInverseMatrix() }; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ed9d330934..508e67ec17 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -77,9 +77,11 @@ public: Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); - void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth = 1.0f, float farDepth = 1000.0f); - void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); + void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float nearDepth = 1.0f, float farDepth = 1000.0f); + void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d362c14df8..b83f2e87e9 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -136,7 +136,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum); + shadow->setCascadeFrustum(_cascadeIndex, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -213,9 +213,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } + task.addJob("ShadowSetup"); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowSetup", i); - const auto shadowFilter = setupOutput.getN(1); + const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + const auto shadowFilter = setupOutput.getN(1); // CPU jobs: // Fetch and cull the items from the scene @@ -229,7 +231,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", setupOutput); + task.addJob("ShadowCascadeTeardown", setupOutput); } } @@ -239,7 +241,19 @@ void RenderShadowTask::configure(const Config& configuration) { // Task::configure(configuration); } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { + auto lightStage = renderContext->_scene->getStage(); + assert(lightStage); + // Cache old render args + RenderArgs* args = renderContext->args; + + const auto globalShadow = lightStage->getCurrentKeyShadow(); + if (globalShadow) { + globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + } +} + +void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); // Cache old render args @@ -251,7 +265,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); @@ -261,7 +275,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O } } -void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { +void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 3dcdfdd9b1..a723a529f0 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -55,10 +55,19 @@ public: class RenderShadowSetup { public: - using Outputs = render::VaryingSet2; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::Model; - RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + RenderShadowSetup() {} + void run(const render::RenderContextPointer& renderContext); + +}; + +class RenderShadowCascadeSetup { +public: + using Outputs = render::VaryingSet2; + using JobModel = render::Job::ModelO; + + RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -66,10 +75,10 @@ private: unsigned int _cascadeIndex; }; -class RenderShadowTeardown { +class RenderShadowCascadeTeardown { public: - using Input = RenderShadowSetup::Outputs; - using JobModel = render::Job::ModelI; + using Input = RenderShadowCascadeSetup::Outputs; + using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; From dc55a168342e2616d584d89d84d82fab63390479 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 18:37:27 +0100 Subject: [PATCH 090/137] Fixed warnings on Mac and Ubuntu --- libraries/render-utils/src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 4 ++-- libraries/render-utils/src/RenderDeferredTask.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8bbd212a25..3286531643 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -503,7 +503,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Bind the shadow buffers if (globalShadow) { - for (auto i = 0; i < globalShadow->getCascadeCount(); i++) { + for (unsigned int i = 0; i < globalShadow->getCascadeCount(); i++) { batch.setResourceTexture(SHADOW_MAP_UNIT+i, globalShadow->getCascade(i).map); } } diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 95629b5c3d..b9b774452c 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -131,11 +131,11 @@ void LightStage::Shadow::setMaxDistance(float value) { float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; float minCascadeDistance = 0.0f; - for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { float blendFactor = cascadeIndex / float(_cascades.size() - 1); float maxCascadeDistance; - if (cascadeIndex == _cascades.size() - 1) { + if (cascadeIndex == size_t(_cascades.size() - 1)) { maxCascadeDistance = _maxDistance; } else { maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bac28c0ddd..5c9abbabed 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -566,7 +566,7 @@ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Out if (lightStage) { auto globalShadow = lightStage->getCurrentKeyShadow(); - if (globalShadow && igetCascadeCount()) { + if (globalShadow && i<(int)globalShadow->getCascadeCount()) { auto& cascade = globalShadow->getCascade(i); shadowFrustum = cascade.getFrustum(); } else { From 0146eca31506fc20e1f59176253627e4c100ff8a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Dec 2017 10:09:05 +1300 Subject: [PATCH 091/137] JSDoc review --- interface/src/ui/overlays/Base3DOverlay.cpp | 4 +-- interface/src/ui/overlays/Billboardable.cpp | 2 +- interface/src/ui/overlays/Circle3DOverlay.cpp | 25 +++++++++-------- interface/src/ui/overlays/Cube3DOverlay.cpp | 17 ++++++----- interface/src/ui/overlays/Grid3DOverlay.cpp | 17 ++++++----- interface/src/ui/overlays/Image3DOverlay.cpp | 24 +++++++++------- interface/src/ui/overlays/ImageOverlay.cpp | 2 +- interface/src/ui/overlays/Line3DOverlay.cpp | 17 ++++++----- interface/src/ui/overlays/ModelOverlay.cpp | 19 +++++++------ interface/src/ui/overlays/Overlay.cpp | 15 ++++++---- interface/src/ui/overlays/Overlays.cpp | 8 ++++-- interface/src/ui/overlays/Overlays.h | 28 +++++++++++-------- .../src/ui/overlays/Rectangle3DOverlay.cpp | 17 ++++++----- .../src/ui/overlays/RectangleOverlay.cpp | 2 +- interface/src/ui/overlays/Shape3DOverlay.cpp | 17 ++++++----- interface/src/ui/overlays/Sphere3DOverlay.cpp | 17 ++++++----- interface/src/ui/overlays/Text3DOverlay.cpp | 19 +++++++------ interface/src/ui/overlays/TextOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 21 ++++++++------ 19 files changed, 159 insertions(+), 114 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 752ac75511..5b29279dba 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -214,12 +214,12 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp index a0e5b27449..4ca024f2e5 100644 --- a/interface/src/ui/overlays/Billboardable.cpp +++ b/interface/src/ui/overlays/Billboardable.cpp @@ -25,7 +25,7 @@ void Billboardable::setProperties(const QVariantMap& properties) { // JSDoc for copying to @typedefs of overlay types that inherit Billboardable. /**jsdoc - * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. */ QVariant Billboardable::getProperty(const QString &property) { diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 8404b771ab..600c1cefa9 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -368,7 +368,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { } } -// Overlay's color and alpha properties are overridden. +// Overlay's color and alpha properties are overridden. And the dimensions property is not used. /**jsdoc * These are the properties of a circle3d {@link Overlays.OverlayType|OverlayType}. * @typedef {object} Overlays.Circle3DProperties @@ -376,14 +376,17 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * @property {string} type=circle3d - Has the value "circle3d". Read-only. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -399,12 +402,12 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if @@ -412,7 +415,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. * Not used. - * + * * @property {number} startAt=0 - The counter-clockwise angle from the overlay's x-axis that drawing starts at, in degrees. * @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees. * @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: radius. @@ -447,12 +450,12 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. Write-only. * @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. Write-only. - * @property {boolean} hasTickMarks=false - If true then tick marks are drawn. + * @property {boolean} hasTickMarks=false - If true, tick marks are drawn. * @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees. * @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees. * @property {number} majorTickMarksLength=0 - The length of the major tick marks, in meters. A positive value draws tick marks * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {number} minorTickMarksLength=0 - The length of the minor tick arks, in meters. A positive value draws tick marks + * @property {number} minorTickMarksLength=0 - The length of the minor tick marks, in meters. A positive value draws tick marks * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. * @property {Color} majorTickMarksColor=0,0,0 - The color of the major tick marks. * @property {Color} minorTickMarksColor=0,0,0 - The color of the minor tick marks. diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 5e1179a2ae..e34e68e053 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -143,14 +143,17 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -166,12 +169,12 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index cbb64d9275..e06fa22692 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -121,14 +121,17 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -144,12 +147,12 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 9f5c3e8116..454945d31c 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -189,7 +189,7 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { } /**jsdoc - * These are the properties of a image3d {@link Overlays.OverlayType|OverlayType}. + * These are the properties of an image3d {@link Overlays.OverlayType|OverlayType}. * @typedef {object} Overlays.Image3DProperties * * @property {string} type=image3d - Has the value "image3d". Read-only. @@ -197,14 +197,17 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -220,12 +223,12 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if @@ -233,12 +236,13 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. * - * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. * * @property {string} url - The URL of the PNG or JPG image to display. * @property {Rect} subImage - The portion of the image to display. Defaults to the full image. - * @property {boolean} emissive - If true then the overlay is displayed at full brightness. + * @property {boolean} emissive - If true, the overlay is displayed at full brightness, otherwise it is rendered + * with scene lighting. */ QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "url") { diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 0faa27b866..19f32511f6 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -38,7 +38,7 @@ QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml")); * @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. Write-only. * @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, 0.0 - * 1.0. Write-only. - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * Write-only. */ diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 3f8d58ac3c..7a4aa76b5d 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -254,14 +254,17 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -277,12 +280,12 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 640299f234..ddf2e2579c 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -276,14 +276,17 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -299,12 +302,12 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if @@ -327,7 +330,7 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { * @property {number} animationSettings.lastFrame=0 - The frame to finish playing at. * @property {boolean} animationSettings.running=false - Whether or not the animation is playing. * @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop. - * @property {boolean} animationSettings.hold=false - Whether or not the when the animation finishes, the rotations and + * @property {boolean} animationSettings.hold=false - Whether or not when the animation finishes, the rotations and * translations of the last frame played should be maintained. * @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should * be played. diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index b17a462ebb..400dab8f14 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -101,21 +101,24 @@ void Overlay::setProperties(const QVariantMap& properties) { } } -// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. +// JSDoc for copying to @typedefs of overlay types that inherit Overlay. /**jsdoc * @property {string} type=TODO - Has the value "TODO". Read-only. * @property {Color} color=255,255,255 - The color of the overlay. * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. */ diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index f75735c6f8..052ef0b6d8 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -180,7 +180,8 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) * * * circle3d3DA circle. - * cube3DA cube. Can also use a shape to create a cube. + * cube3DA cube. Can also use a shape overlay to create a + * cube. * grid3DA grid of lines in a plane. * image2DAn image. Synonym: billboard. * image3d3DAn image. @@ -189,12 +190,15 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) * rectangle2DA rectangle. * rectangle3d3DA rectangle. * shape3DA geometric shape, such as a cube, sphere, or cylinder. - * sphere3DA sphere. Can also use a shape to create a sphere. + * sphere3DA sphere. Can also use a shape overlay to create a + * sphere. * text2DText. * text3d3DText. * web3d3DWeb content. * * + *

2D overlays are rendered on the display surface in desktop mode and on the HUD surface in HMD mode. 3D overlays are + * rendered at a position and orientation in-world.

*

Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.

* @typedef {string} Overlays.OverlayType */ diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3e668df48b..9346bd30f3 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -75,7 +75,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R * yourself and that aren't persisted to the domain. They are used for UI. * @namespace Overlays * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus. - * If no overlay is set, get returns null; set null to clear keyboard focus. + * If no overlay is set, get returns null; set to null to clear keyboard focus. */ class Overlays : public QObject { @@ -171,9 +171,10 @@ public slots: /**jsdoc * Edit multiple overlays' properties. * @function Overlays.editOverlays - * @param propertiesById {object.} - On object with overlay IDs as keys and - * {@link Overlays.OverlayProperties|OverlayProperties} edits to make for each as values. - * @returns {boolean} true if all overlays were found and edited, otherwise false. + * @param propertiesById {object.} - An object with overlay IDs as keys and + * {@link Overlays.OverlayProperties|OverlayProperties} edits to make as values. + * @returns {boolean} true if all overlays were found and edited, otherwise false (some may have + * been found and edited). * @example Create two overlays in front of your avatar then change their colors. * var overlayA = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })), @@ -429,8 +430,8 @@ public slots: * @param {Uuid} overlayID - The ID of the overlay to use for calculation. * @param {string} text - The string to calculate the size of. * @returns {Size} The size of the text if the overlay is a text overlay, otherwise - * { height: 0, width : 0 }. If the overlay is a 2D overlay the size is in pixels; if the overlay if a 3D - * overlay the size is in meters. + * { height: 0, width : 0 }. If the overlay is a 2D overlay, the size is in pixels; if the overlay is a 3D + * overlay, the size is in meters. * @example Calculate the size of "hello" in a 3D text overlay. * var overlay = Overlays.addOverlay("text3d", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })), @@ -541,7 +542,7 @@ public slots: * Generate a mouse move event on an overlay. * @function Overlays.sendMouseMoveOnOverlay * @param {Uuid} overlayID - The ID of the overlay to generate a mouse move event on. - * @param {PointerEvent} event - The mouse moved event details. + * @param {PointerEvent} event - The mouse move event details. */ void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); @@ -627,16 +628,19 @@ signals: * @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on. * @param {PointerEvent} event - The mouse press event details. * @returns {Signal} - * // Create a cube overlay in front of your avatar and report mouse clicks on it. + * @example Create a cube overlay in front of your avatar and report mouse clicks on it. * var overlay = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), * rotation: MyAvatar.orientation, * dimensions: { x: 0.3, y: 0.3, z: 0.3 }, * solid: true * }); - * print("Overlay: " + overlay); + * print("My overlay: " + overlay); + * * Overlays.mousePressOnOverlay.connect(function(overlayID, event) { - * print("Clicked: " + overlayID); + * if (overlayID === overlay) { + * print("Clicked on my overlay"); + * } * }); */ void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); @@ -671,14 +675,14 @@ signals: void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); /**jsdoc - * Triggered when a mouse press event occurs on something other than an overlay. Only occurs for 3D overlays. + * Triggered when a mouse press event occurs on something other than a 3D overlay. * @function Overlays.mousePressOffOverlay * @returns {Signal} */ void mousePressOffOverlay(); /**jsdoc - * Triggered when a mouse double press event occurs on something other than an overlay. Only occurs for 3D overlays. + * Triggered when a mouse double press event occurs on something other than a 3D overlay. * @function Overlays.mouseDoublePressOffOverlay * @returns {Signal} */ diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 6be5cf0936..425b7e8155 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -116,14 +116,17 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -139,12 +142,12 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/RectangleOverlay.cpp b/interface/src/ui/overlays/RectangleOverlay.cpp index 0b19297464..af37a4ac02 100644 --- a/interface/src/ui/overlays/RectangleOverlay.cpp +++ b/interface/src/ui/overlays/RectangleOverlay.cpp @@ -30,7 +30,7 @@ QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml")); * @property {Color} borderColor=0,0,0 - The color of the border. Write-only. * @property {number} borderAlpha=1.0 - The opacity of the border, 0.0 - 1.0. * Write-only. - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * Write-only. */ diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index cfcd14a5d6..afc2f8d0b4 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -116,14 +116,17 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -139,12 +142,12 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 5fe5ff0541..8cb7f7757b 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -36,14 +36,17 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -59,12 +62,12 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 2dfbf22b9d..a66b57c365 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -213,14 +213,17 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -236,12 +239,12 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if @@ -249,7 +252,7 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. * - * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. * * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index e937481823..fba36f0c77 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -50,7 +50,7 @@ QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml")); * @property {number} alpha=1.0 - The opacity of the overlay, 0.0 - 1.0. Write-only. * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. Write-only. * @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. Write-only. - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * Write-only. */ diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 90b7869a58..859ecd3230 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -481,14 +481,17 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. - * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse goes from + * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the - * current pulse value each frame. (The magnitude of the property isn't otherwise used.) + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the - * current pulse value each frame. If > 0 the pulse is applied in phase with the alpha pulse; if < 0 the pulse is applied - * out of phase with the alpha pulse. (The magnitude of the property isn't otherwise used.) - * @property {boolean} visible=true - If true the overlay is rendered, otherwise it is not rendered. + * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 + * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise + * used.) + * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * @@ -504,22 +507,22 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. - * @property {boolean} isDashedLine=false - If true a dashed line is drawn on the overlay's edges. Synonym: + * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't - * have drawInFront set to true and in front of entities. + * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * parentID is an avatar skeleton. A value of 65535 means "no joint". * - * @property {boolean} isFacingAvatar - If true then the overlay is rotated to face the user's camera about an axis + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. * * @property {string} url - The URL of the Web page to display. - * @property {string} scriptURL="" - The URL of a file of JavaScript to inject into the Web page. + * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page. * @property {Vec2} resolution=640,480 - The number of pixels. Deprecated: This property is being removed. * @property {number} dpi=30.47 - The dots per inch to display the Web page at. * @property {Vec2} dimensions=1,1 - Scales the size of the overlay calculated from resolution and From 8b38c03a5624f3fab6981214964628e227021667 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 7 Dec 2017 17:40:58 -0800 Subject: [PATCH 092/137] Allow space characters in material library name --- libraries/fbx/src/OBJReader.cpp | 6 +++--- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index e0c2efd72e..315c6a86d2 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -56,7 +56,7 @@ float OBJTokenizer::getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } -int OBJTokenizer::nextToken() { +int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; _pushedBackToken = NO_PUSHBACKED_TOKEN; @@ -93,7 +93,7 @@ int OBJTokenizer::nextToken() { _datum = ""; _datum.append(ch); while (_device->getChar(&ch)) { - if (QChar(ch).isSpace() || ch == '\"') { + if ((QChar(ch).isSpace() || ch == '\"') && (!allowSpaceChar || ch != ' ')) { ungetChar(ch); // read until we encounter a special character, then replace it break; } @@ -399,7 +399,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi currentMaterialName = QString("part-") + QString::number(_partCounter++); } } else if (token == "mtllib" && !_url.isEmpty()) { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + if (tokenizer.nextToken(true) != OBJTokenizer::DATUM_TOKEN) { break; } QByteArray libraryName = tokenizer.getDatum(); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index fb250833cf..45e3f79480 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -11,7 +11,7 @@ public: DATUM_TOKEN = 0x100, COMMENT_TOKEN = 0x101 }; - int nextToken(); + int nextToken(bool allowSpaceChar = false); const QByteArray& getDatum() const { return _datum; } bool isNextTokenFloat(); const QByteArray getLineAsDatum(); // some "filenames" have spaces in them From 46583a90be582da995a220124db9dbf96e8a5bd3 Mon Sep 17 00:00:00 2001 From: Daniela Fontes Date: Sat, 9 Dec 2017 00:14:36 +0000 Subject: [PATCH 093/137] Change abs to fabsf and other corrections requested. --- interface/src/avatar/MyAvatar.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 28ecb07d31..3475b0c940 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -81,6 +81,7 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; +const float MIN_SCALE_CHANGED_DELTA = 0.001f; MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), @@ -670,8 +671,7 @@ void MyAvatar::updateSensorToWorldMatrix() { _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); bool hasSensorToWorldScaleChanged = false; - - if (abs(AvatarData::getSensorToWorldScale() - sensorToWorldScale) > 0.001f) { + if (fabsf(getSensorToWorldScale() - sensorToWorldScale) > MIN_SCALE_CHANGED_DELTA) { hasSensorToWorldScaleChanged = true; } @@ -683,13 +683,11 @@ void MyAvatar::updateSensorToWorldMatrix() { } _sensorToWorldMatrixCache.set(_sensorToWorldMatrix); - updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache); updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); if (hasSensorToWorldScaleChanged) { emit sensorToWorldScaleChanged(sensorToWorldScale); - //qDebug() << "Debug: emit sensorToWorldScaleChanged " << sensorToWorldScale; } } From cb471582e57fde50d1e60112b3b613179afccae1 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sun, 10 Dec 2017 01:34:03 +0300 Subject: [PATCH 094/137] 10334 Unable to type in Go To app search bar after UI reappears from moving Avatar --- .../qml/hifi/tablet/TabletAddressDialog.qml | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 6aa3b8e7fe..160151144b 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -20,6 +20,8 @@ import "../" import "../toolbars" import "../../styles-uit" as HifiStyles import "../../controls-uit" as HifiControls +import QtQuick.Controls 2.2 as QQC2 +import QtQuick.Templates 2.2 as T // references HMD, AddressManager, AddressBarDialog from root context @@ -223,7 +225,7 @@ StackView { visible: addressLine.text.length === 0 } - TextField { + QQC2.TextField { id: addressLine width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin; anchors { @@ -238,16 +240,36 @@ StackView { addressBarDialog.keyboardEnabled = false; toggleOrGo(); } - placeholderText: "Type domain address here" + + // unfortunately TextField from Quick Controls 2 disallow customization of placeHolderText color without creation of new style + property string placeholderText2: "Type domain address here" verticalAlignment: TextInput.AlignBottom - style: TextFieldStyle { - textColor: hifi.colors.text - placeholderTextColor: "gray" - font { - family: hifi.fonts.fontFamily - pixelSize: hifi.fonts.pixelSize * 0.75 + + font { + family: hifi.fonts.fontFamily + pixelSize: hifi.fonts.pixelSize * 0.75 + } + + color: hifi.colors.text + background: Item {} + + QQC2.Label { + T.TextField { + id: control + + padding: 6 // numbers taken from Qt\5.9.2\Src\qtquickcontrols2\src\imports\controls\TextField.qml + leftPadding: padding + 4 } - background: Item {} + + font: parent.font + + x: control.leftPadding + y: control.topPadding + + text: parent.placeholderText2 + verticalAlignment: "AlignVCenter" + color: 'gray' + visible: parent.text === '' } } From b9d49318e76201774437292659f985065d888661 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 11 Dec 2017 12:19:10 +0100 Subject: [PATCH 095/137] Fixed bug which culled medium sized objects in shadow maps due to perspective frustum assumed octree selection --- libraries/render-utils/src/RenderShadowTask.cpp | 10 ++++++++++ libraries/render-utils/src/RenderShadowTask.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b83f2e87e9..2e5b7132e4 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,6 +260,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon RenderArgs* args = renderContext->args; output.edit0() = args->_renderMode; + output.edit2() = args->_sizeScale; const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { @@ -270,6 +271,14 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + if (lightStage->getCurrentKeyLight()->getType() == model::Light::SUN) { + const float shadowSizeScale = 1e16f; + // Set the size scale to a ridiculously high value to prevent small object culling which assumes + // the view frustum is a perspective projection. But this isn't the case for the sun which + // is an orthographic projection. + args->_sizeScale = shadowSizeScale; + } + } else { output.edit1() = ItemFilter::Builder::nothing(); } @@ -284,4 +293,5 @@ void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& render assert(args->hasViewFrustum()); // Reset the render args args->_renderMode = input.get0(); + args->_sizeScale = input.get2(); }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index a723a529f0..d8d4c624e7 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -64,7 +64,7 @@ public: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} From 068d04c58da8842ad2272e4280635573fcee81e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 6 Dec 2017 16:49:27 -0800 Subject: [PATCH 096/137] abstraction of recursion through bullet perf stats --- libraries/physics/src/PhysicsEngine.cpp | 52 ++++++++++++++----------- libraries/physics/src/PhysicsEngine.h | 1 - 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a64796308e..9ff6a59255 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -10,6 +10,7 @@ // #include +#include #include @@ -328,6 +329,33 @@ void PhysicsEngine::stepSimulation() { } } +using CProfileOperator = std::function; + +CProfileOperator harvestProfile = [](CProfileIterator* profileIterator, QString contextName) { + QString childContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Name()); + uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time()); + PerformanceTimer::addTimerRecord(childContextName, time); + }; + +void recurseOpOnPerformanceStats(CProfileOperator op, CProfileIterator* profileIterator, QString contextName) { + QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name()); + // get the stats for the children + int32_t numChildren = 0; + profileIterator->First(); + while (!profileIterator->Is_Done()) { + op(profileIterator, contextName); + profileIterator->Next(); + ++numChildren; + } + // recurse the children + for (int32_t i = 0; i < numChildren; ++i) { + profileIterator->Enter_Child(i); + recurseOpOnPerformanceStats(op, profileIterator, parentContextName); + } + // retreat back to parent + profileIterator->Enter_Parent(); +} + void PhysicsEngine::harvestPerformanceStats() { // unfortunately the full context names get too long for our stats presentation format //QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name? @@ -340,7 +368,7 @@ void PhysicsEngine::harvestPerformanceStats() { for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) { if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") { profileIterator->Enter_Child(childIndex); - recursivelyHarvestPerformanceStats(profileIterator, contextName); + recurseOpOnPerformanceStats(harvestProfile, profileIterator, contextName); break; } profileIterator->Next(); @@ -348,27 +376,6 @@ void PhysicsEngine::harvestPerformanceStats() { } } -void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) { - QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name()); - // get the stats for the children - int32_t numChildren = 0; - profileIterator->First(); - while (!profileIterator->Is_Done()) { - QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name()); - uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time()); - PerformanceTimer::addTimerRecord(childContextName, time); - profileIterator->Next(); - ++numChildren; - } - // recurse the children - for (int32_t i = 0; i < numChildren; ++i) { - profileIterator->Enter_Child(i); - recursivelyHarvestPerformanceStats(profileIterator, contextName); - } - // retreat back to parent - profileIterator->Enter_Parent(); -} - void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { BT_PROFILE("ownershipInfection"); @@ -515,6 +522,7 @@ const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { void PhysicsEngine::dumpStatsIfNecessary() { if (_dumpNextStats) { _dumpNextStats = false; + CProfileManager::Increment_Frame_Counter(); CProfileManager::dumpAll(); } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 3063a4a89a..4e63e8ff3e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -94,7 +94,6 @@ public: private: QList removeDynamicsForBody(btRigidBody* body); void addObjectToDynamicsWorld(ObjectMotionState* motionState); - void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName); /// \brief bump any objects that touch this one, then remove contact info void bumpAndPruneContacts(ObjectMotionState* motionState); From fcff9d7e369bb2b8e5c3f4233c9901d942d63465 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 7 Dec 2017 08:58:35 -0800 Subject: [PATCH 097/137] use class hierarchy instead of lambdas --- libraries/physics/src/PhysicsEngine.cpp | 33 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 9ff6a59255..e73b9035e6 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -329,28 +329,38 @@ void PhysicsEngine::stepSimulation() { } } -using CProfileOperator = std::function; - -CProfileOperator harvestProfile = [](CProfileIterator* profileIterator, QString contextName) { - QString childContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Name()); - uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time()); - PerformanceTimer::addTimerRecord(childContextName, time); +class CProfileOperator { +public: + CProfileOperator(QString context) : _context(context) { }; + virtual void process(CProfileIterator*) const = 0; +protected: + QString _context; +}; -void recurseOpOnPerformanceStats(CProfileOperator op, CProfileIterator* profileIterator, QString contextName) { - QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name()); +class PhysicsStatsHarvester : public CProfileOperator { +public: + PhysicsStatsHarvester() : CProfileOperator("...") {} + void process(CProfileIterator* itr) const override { + QString name = _context + QString("/") + QString(itr->Get_Current_Name()); + uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Total_Time()); + PerformanceTimer::addTimerRecord(name, time); + }; +}; + +void recurseOpOnPerformanceStats(const CProfileOperator& op, CProfileIterator* profileIterator) { // get the stats for the children int32_t numChildren = 0; profileIterator->First(); while (!profileIterator->Is_Done()) { - op(profileIterator, contextName); + op.process(profileIterator); profileIterator->Next(); ++numChildren; } // recurse the children for (int32_t i = 0; i < numChildren; ++i) { profileIterator->Enter_Child(i); - recurseOpOnPerformanceStats(op, profileIterator, parentContextName); + recurseOpOnPerformanceStats(op, profileIterator); } // retreat back to parent profileIterator->Enter_Parent(); @@ -368,7 +378,8 @@ void PhysicsEngine::harvestPerformanceStats() { for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) { if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") { profileIterator->Enter_Child(childIndex); - recurseOpOnPerformanceStats(harvestProfile, profileIterator, contextName); + PhysicsStatsHarvester harvester; + harvester.process(profileIterator); break; } profileIterator->Next(); From 4540e9e483a2f4803a51854a8fdb070fe5774cc4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 7 Dec 2017 13:08:34 -0800 Subject: [PATCH 098/137] add ability to save simulation stats to file --- libraries/physics/src/PhysicsEngine.cpp | 116 +++++++++++++++++------- libraries/physics/src/PhysicsEngine.h | 1 + 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index e73b9035e6..2eec4a3950 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -10,8 +10,11 @@ // #include + #include +#include + #include #include "CharacterController.h" @@ -331,58 +334,103 @@ void PhysicsEngine::stepSimulation() { class CProfileOperator { public: - CProfileOperator(QString context) : _context(context) { - }; - virtual void process(CProfileIterator*) const = 0; -protected: - QString _context; + CProfileOperator() {} + void recurse(CProfileIterator* itr, QString context) { + // The context string will get too long if we accumulate it properly + //QString newContext = context + QString("/") + itr->Get_Current_Parent_Name(); + // so we use this four-character indentation + QString newContext = context + QString(".../"); + process(itr, newContext); + + // count the children + int32_t numChildren = 0; + itr->First(); + while (!itr->Is_Done()) { + itr->Next(); + ++numChildren; + } + + // recurse the children + if (numChildren > 0) { + // recurse the children + for (int32_t i = 0; i < numChildren; ++i) { + itr->Enter_Child(i); + recurse(itr, newContext); + } + } + // retreat back to parent + itr->Enter_Parent(); + } + virtual void process(CProfileIterator*, QString context) = 0; }; -class PhysicsStatsHarvester : public CProfileOperator { +class StatsHarvester : public CProfileOperator { public: - PhysicsStatsHarvester() : CProfileOperator("...") {} - void process(CProfileIterator* itr) const override { - QString name = _context + QString("/") + QString(itr->Get_Current_Name()); - uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Total_Time()); + StatsHarvester() {} + void process(CProfileIterator* itr, QString context) override { + QString name = context + itr->Get_Current_Parent_Name(); + uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time()); PerformanceTimer::addTimerRecord(name, time); }; }; -void recurseOpOnPerformanceStats(const CProfileOperator& op, CProfileIterator* profileIterator) { - // get the stats for the children - int32_t numChildren = 0; - profileIterator->First(); - while (!profileIterator->Is_Done()) { - op.process(profileIterator); - profileIterator->Next(); - ++numChildren; +class StatsWriter : public CProfileOperator { +public: + StatsWriter(QString filename) : _file(filename) { + _file.open(QFile::WriteOnly); + if (_file.error() != QFileDevice::NoError) { + qCDebug(physics) << "unable to open file " << _file.fileName() << " to save stepSimulation() stats"; + } } - // recurse the children - for (int32_t i = 0; i < numChildren; ++i) { - profileIterator->Enter_Child(i); - recurseOpOnPerformanceStats(op, profileIterator); + ~StatsWriter() { + _file.close(); } - // retreat back to parent - profileIterator->Enter_Parent(); -} + void process(CProfileIterator* itr, QString context) override { + QString name = context + itr->Get_Current_Parent_Name(); + float time = (btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time(); + if (_file.error() == QFileDevice::NoError) { + QTextStream s(&_file); + s << name << " = " << time << "\n"; + } + } +protected: + QFile _file; +}; void PhysicsEngine::harvestPerformanceStats() { // unfortunately the full context names get too long for our stats presentation format //QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name? QString contextName("..."); - CProfileIterator* profileIterator = CProfileManager::Get_Iterator(); - if (profileIterator) { + CProfileIterator* itr = CProfileManager::Get_Iterator(); + if (itr) { // hunt for stepSimulation context - profileIterator->First(); - for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) { - if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") { - profileIterator->Enter_Child(childIndex); - PhysicsStatsHarvester harvester; - harvester.process(profileIterator); + itr->First(); + for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) { + if (QString(itr->Get_Current_Name()) == "stepSimulation") { + itr->Enter_Child(childIndex); + StatsHarvester harvester; + harvester.recurse(itr, "bt"); break; } - profileIterator->Next(); + itr->Next(); + } + } +} + +void PhysicsEngine::printPerformanceStatsToFile(const QString& filename) { + CProfileIterator* itr = CProfileManager::Get_Iterator(); + if (itr) { + // hunt for stepSimulation context + itr->First(); + for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) { + if (QString(itr->Get_Current_Name()) == "stepSimulation") { + itr->Enter_Child(childIndex); + StatsWriter writer(filename); + writer.recurse(itr, ""); + break; + } + itr->Next(); } } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 4e63e8ff3e..3956dd09d9 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -62,6 +62,7 @@ public: void stepSimulation(); void harvestPerformanceStats(); + void printPerformanceStatsToFile(const QString& filename); void updateContactMap(); bool hasOutgoingChanges() const { return _hasOutgoingChanges; } From 2538204b1eb05d8dd5632e5a530be0d2623ec503 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 7 Dec 2017 16:52:21 -0800 Subject: [PATCH 099/137] expose physics simulation stats to Test JS API --- interface/src/Application.cpp | 7 +++++-- interface/src/Application.h | 2 ++ interface/src/scripting/TestScriptingInterface.cpp | 4 ++++ interface/src/scripting/TestScriptingInterface.h | 5 +++++ libraries/physics/src/PhysicsEngine.cpp | 13 +++++++++++++ libraries/physics/src/PhysicsEngine.h | 9 +++++++-- libraries/physics/src/ThreadSafeDynamicsWorld.cpp | 8 +++++++- 7 files changed, 43 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0176acf108..3745a82b3c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3222,8 +3222,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } } - - void Application::keyReleaseEvent(QKeyEvent* event) { _keysPressed.remove(event->key()); @@ -7506,4 +7504,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; } + +void Application::saveNextPhysicsStats(QString filename) { + _physicsEngine->saveNextPhysicsStats(filename); +} + #include "Application.moc" diff --git a/interface/src/Application.h b/interface/src/Application.h index 9542c5ccb6..ee16740f20 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -280,6 +280,7 @@ public: void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; } QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; } bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } + void saveNextPhysicsStats(QString filename); signals: void svoImportRequested(const QString& url); @@ -432,6 +433,7 @@ private slots: void handleSandboxStatus(QNetworkReply* reply); void switchDisplayMode(); + private: static void initDisplay(); void init(); diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index d4b4ba1480..b53b9d5d5f 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -141,6 +141,10 @@ void TestScriptingInterface::endTraceEvent(QString name) { tracing::traceEvent(trace_test(), name, tracing::DurationEnd); } +void TestScriptingInterface::savePhysicsSimulationStats(QString filename) { + qApp->saveNextPhysicsStats(filename); +} + void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) { PROFILE_RANGE(script, name); fn.call(); diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 73b8f0ac93..aca07d110b 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -71,6 +71,11 @@ public slots: void endTraceEvent(QString name); + /**jsdoc + * Write detailed timing stats of next physics stepSimulation() to filename + */ + void savePhysicsSimulationStats(QString filename); + Q_INVOKABLE void profileRange(const QString& name, QScriptValue function); private: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 2eec4a3950..36b677e92f 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "CharacterController.h" #include "ObjectMotionState.h" @@ -294,6 +295,7 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { + DETAILED_PROFILE_RANGE(simulation_physics, "avatarController"); BT_PROFILE("avatarController"); // TODO: move this stuff outside and in front of stepSimulation, because // the updateShapeIfNecessary() call needs info from MyAvatar and should @@ -465,6 +467,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const } void PhysicsEngine::updateContactMap() { + DETAILED_PROFILE_RANGE(simulation_physics, "updateContactMap"); BT_PROFILE("updateContactMap"); ++_numContactFrames; @@ -582,10 +585,20 @@ void PhysicsEngine::dumpStatsIfNecessary() { if (_dumpNextStats) { _dumpNextStats = false; CProfileManager::Increment_Frame_Counter(); + if (_saveNextStats) { + _saveNextStats = false; + printPerformanceStatsToFile(_statsFilename); + } CProfileManager::dumpAll(); } } +void PhysicsEngine::saveNextPhysicsStats(QString filename) { + _saveNextStats = true; + _dumpNextStats = true; + _statsFilename = filename; +} + // Bullet collision flags are as follows: // CF_STATIC_OBJECT= 1, // CF_KINEMATIC_OBJECT= 2, diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 3956dd09d9..6619a5489d 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -77,6 +77,9 @@ public: /// \brief prints timings for last frame if stats have been requested. void dumpStatsIfNecessary(); + /// \brief saves timings for last frame in filename + void saveNextPhysicsStats(QString filename); + /// \param offset position of simulation origin in domain-frame void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; } @@ -116,6 +119,7 @@ private: QHash _objectDynamics; QHash> _objectDynamicsByBody; std::set _activeStaticBodies; + QString _statsFilename; glm::vec3 _originOffset; @@ -124,8 +128,9 @@ private: uint32_t _numContactFrames = 0; uint32_t _numSubsteps; - bool _dumpNextStats = false; - bool _hasOutgoingChanges = false; + bool _dumpNextStats { false }; + bool _saveNextStats { false }; + bool _hasOutgoingChanges { false }; }; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 24cfbc2609..abda5c82fc 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -18,6 +18,7 @@ #include #include "ThreadSafeDynamicsWorld.h" +#include "Profile.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( btDispatcher* dispatcher, @@ -29,6 +30,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "stepWithCB"); BT_PROFILE("stepSimulationWithSubstepCallback"); int subSteps = 0; if (maxSubSteps) { @@ -68,11 +70,13 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep saveKinematicState(fixedTimeStep*clampedSimulationSteps); { + DETAILED_PROFILE_RANGE(simulation_physics, "applyGravity"); BT_PROFILE("applyGravity"); applyGravity(); } for (int i=0;i Date: Mon, 11 Dec 2017 11:27:33 -0800 Subject: [PATCH 100/137] cleanup prof stats and trace context names --- interface/src/Application.cpp | 18 +++++------- .../src/EntityTreeRenderer.cpp | 10 +++---- libraries/entities/src/EntityItem.cpp | 3 ++ libraries/entities/src/EntitySimulation.cpp | 12 +++----- libraries/entities/src/EntityTree.cpp | 24 ++++++++------- libraries/physics/src/EntityMotionState.cpp | 7 ++++- .../physics/src/PhysicalEntitySimulation.cpp | 29 ++++++++++++------- libraries/physics/src/PhysicsEngine.cpp | 2 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 2 +- 9 files changed, 59 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3745a82b3c..ed1a9d41d2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4846,8 +4846,7 @@ void Application::update(float deltaTime) { if (_physicsEnabled) { { PROFILE_RANGE(simulation_physics, "PreStep"); - - PerformanceTimer perfTimer("updateStates)"); + PerformanceTimer perfTimer("preStep)"); static VectorOfMotionStates motionStates; _entitySimulation->getObjectsToRemoveFromPhysics(motionStates); _physicsEngine->removeObjects(motionStates); @@ -4880,22 +4879,22 @@ void Application::update(float deltaTime) { } { PROFILE_RANGE(simulation_physics, "Step"); - PerformanceTimer perfTimer("stepSimulation"); + PerformanceTimer perfTimer("step"); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); } { PROFILE_RANGE(simulation_physics, "PostStep"); - PerformanceTimer perfTimer("harvestChanges"); + PerformanceTimer perfTimer("postStep"); if (_physicsEngine->hasOutgoingChanges()) { // grab the collision events BEFORE handleOutgoingChanges() because at this point // we have a better idea of which objects we own or should own. auto& collisionEvents = _physicsEngine->getCollisionEvents(); getEntities()->getTree()->withWriteLock([&] { - PROFILE_RANGE(simulation_physics, "Harvest"); - PerformanceTimer perfTimer("handleOutgoingChanges"); + PROFILE_RANGE(simulation_physics, "HandleChanges"); + PerformanceTimer perfTimer("handleChanges"); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); _entitySimulation->handleChangedMotionStates(outgoingChanges); @@ -4906,17 +4905,15 @@ void Application::update(float deltaTime) { }); if (!_aboutToQuit) { - // handleCollisionEvents() AFTER handleOutgoinChanges() + // handleCollisionEvents() AFTER handleOutgoingChanges() { PROFILE_RANGE(simulation_physics, "CollisionEvents"); - PerformanceTimer perfTimer("entities"); avatarManager->handleCollisionEvents(collisionEvents); // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk // deadlock.) _entitySimulation->handleCollisionEvents(collisionEvents); } - PROFILE_RANGE(simulation_physics, "UpdateEntities"); // NOTE: the getEntities()->update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). getEntities()->update(true); // update the models... @@ -4927,7 +4924,8 @@ void Application::update(float deltaTime) { myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); } - if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && + if (PerformanceTimer::isActive() && + Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) { _physicsEngine->harvestPerformanceStats(); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5f7899ae74..485d3b68f8 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -251,10 +251,10 @@ void EntityTreeRenderer::shutdown() { } void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) { - PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size()); + PROFILE_RANGE_EX(simulation_physics, "AddToScene", 0xffff00ff, (uint64_t)_entitiesToAdd.size()); PerformanceTimer pt("add"); - // Clear any expired entities - // FIXME should be able to use std::remove_if, but it fails due to some + // Clear any expired entities + // FIXME should be able to use std::remove_if, but it fails due to some // weird compilation error related to EntityItemID assignment operators for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) { if (itr->second.expired()) { @@ -272,7 +272,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r continue; } - // Path to the parent transforms is not valid, + // Path to the parent transforms is not valid, // don't add to the scene graph yet if (!entity->isParentPathComplete()) { continue; @@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r } void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) { - PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size()); + PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size()); PerformanceTimer pt("change"); std::unordered_set changedEntities; _changedEntitiesGuard.withWriteLock([&] { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 48370b02fd..e3abb98a7a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include // usecTimestampNow() #include @@ -984,6 +985,7 @@ void EntityItem::setCollisionSoundURL(const QString& value) { } void EntityItem::simulate(const quint64& now) { + DETAILED_PROFILE_RANGE(simulation_physics, "Simulate"); if (getLastSimulated() == 0) { setLastSimulated(now); } @@ -1039,6 +1041,7 @@ void EntityItem::simulate(const quint64& now) { } bool EntityItem::stepKinematicMotion(float timeElapsed) { + DETAILED_PROFILE_RANGE(simulation_physics, "StepKinematicMotion"); // get all the data Transform transform; glm::vec3 linearVelocity; diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index f91d728d78..36b0d8ab2d 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -29,7 +29,6 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) { } void EntitySimulation::updateEntities() { - PROFILE_RANGE(simulation_physics, "ES::updateEntities"); QMutexLocker lock(&_mutex); quint64 now = usecTimestampNow(); @@ -38,12 +37,7 @@ void EntitySimulation::updateEntities() { callUpdateOnEntitiesThatNeedIt(now); moveSimpleKinematics(now); updateEntitiesInternal(now); - - { - PROFILE_RANGE(simulation_physics, "Sort"); - PerformanceTimer perfTimer("sortingEntities"); - sortEntitiesThatMoved(); - } + sortEntitiesThatMoved(); } void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) { @@ -101,6 +95,7 @@ void EntitySimulation::changeEntityInternal(EntityItemPointer entity) { // protected void EntitySimulation::expireMortalEntities(const quint64& now) { if (now > _nextExpiry) { + PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size()); // only search for expired entities if we expect to find one _nextExpiry = quint64(-1); QMutexLocker lock(&_mutex); @@ -146,6 +141,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { // protected void EntitySimulation::sortEntitiesThatMoved() { + PROFILE_RANGE_EX(simulation_physics, "SortTree", 0xffff00ff, (uint64_t)_entitiesToSort.size()); // NOTE: this is only for entities that have been moved by THIS EntitySimulation. // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. MovingEntitiesOperator moveOperator; @@ -265,7 +261,7 @@ void EntitySimulation::clearEntities() { } void EntitySimulation::moveSimpleKinematics(const quint64& now) { - PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size()); + PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size()); SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin(); while (itemItr != _simpleKinematicEntities.end()) { EntityItemPointer entity = *itemItr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e62399ce95..10fcd19a8c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1770,24 +1770,26 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) { } void EntityTree::update(bool simulate) { - PROFILE_RANGE(simulation_physics, "ET::update"); + PROFILE_RANGE(simulation_physics, "UpdateTree"); fixupNeedsParentFixups(); if (simulate && _simulation) { withWriteLock([&] { _simulation->updateEntities(); - VectorOfEntities pendingDeletes; - _simulation->takeEntitiesToDelete(pendingDeletes); + { + PROFILE_RANGE(simulation_physics, "Deletes"); + VectorOfEntities pendingDeletes; + _simulation->takeEntitiesToDelete(pendingDeletes); + if (pendingDeletes.size() > 0) { + // translate into list of ID's + QSet idsToDelete; - if (pendingDeletes.size() > 0) { - // translate into list of ID's - QSet idsToDelete; + for (auto entity : pendingDeletes) { + idsToDelete.insert(entity->getEntityItemID()); + } - for (auto entity : pendingDeletes) { - idsToDelete.insert(entity->getEntityItemID()); + // delete these things the roundabout way + deleteEntities(idsToDelete, true); } - - // delete these things the roundabout way - deleteEntities(idsToDelete, true); } }); } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7e8b431ceb..a688d521d6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -14,8 +14,9 @@ #include #include #include -#include #include +#include +#include #include "BulletUtil.h" #include "EntityMotionState.h" @@ -325,6 +326,7 @@ bool EntityMotionState::isCandidateForOwnership() const { } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync"); // NOTE: we only get here if we think we own the simulation assert(_body); @@ -476,6 +478,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend"); // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. assert(_entity); @@ -516,6 +519,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { } void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { + DETAILED_PROFILE_RANGE(simulation_physics, "Send"); assert(_entity); assert(entityTreeIsLocked()); @@ -731,6 +735,7 @@ void EntityMotionState::resetMeasuredBodyAcceleration() { } void EntityMotionState::measureBodyAcceleration() { + DETAILED_PROFILE_RANGE(simulation_physics, "MeasureAccel"); // try to manually measure the true acceleration of the object uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); uint32_t numSubsteps = thisStep - _lastMeasureStep; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 3e87b9437d..e4ba47e205 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -10,12 +10,14 @@ // +#include "PhysicalEntitySimulation.h" + +#include #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" -#include "PhysicalEntitySimulation.h" PhysicalEntitySimulation::PhysicalEntitySimulation() { } @@ -274,20 +276,24 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio } void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { + PROFILE_RANGE_EX(simulation_physics, "ChangedEntities", 0x00000000, (uint64_t)motionStates.size()); QMutexLocker lock(&_mutex); // walk the motionStates looking for those that correspond to entities - for (auto stateItr : motionStates) { - ObjectMotionState* state = &(*stateItr); - assert(state); - if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { - EntityMotionState* entityState = static_cast(state); - EntityItemPointer entity = entityState->getEntity(); - assert(entity.get()); - if (entityState->isCandidateForOwnership()) { - _outgoingChanges.insert(entityState); + { + PROFILE_RANGE_EX(simulation_physics, "Filter", 0x00000000, (uint64_t)motionStates.size()); + for (auto stateItr : motionStates) { + ObjectMotionState* state = &(*stateItr); + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { + EntityMotionState* entityState = static_cast(state); + EntityItemPointer entity = entityState->getEntity(); + assert(entity.get()); + if (entityState->isCandidateForOwnership()) { + _outgoingChanges.insert(entityState); + } + _entitiesToSort.insert(entity); } - _entitiesToSort.insert(entity); } } @@ -302,6 +308,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta } // look for entities to prune or update + PROFILE_RANGE_EX(simulation_physics, "Prune/Send", 0x00000000, (uint64_t)_outgoingChanges.size()); QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 36b677e92f..fe794772e2 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -412,7 +412,7 @@ void PhysicsEngine::harvestPerformanceStats() { if (QString(itr->Get_Current_Name()) == "stepSimulation") { itr->Enter_Child(childIndex); StatsHarvester harvester; - harvester.recurse(itr, "bt"); + harvester.recurse(itr, "step/"); break; } itr->Next(); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index abda5c82fc..5b8c0d5843 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -122,7 +122,7 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { } void ThreadSafeDynamicsWorld::synchronizeMotionStates() { - DETAILED_PROFILE_RANGE(simulation_physics, "syncMotionStates"); + PROFILE_RANGE(simulation_physics, "SyncMotionStates"); BT_PROFILE("syncMotionStates"); _changedMotionStates.clear(); From 74a1f5de9643bb06f0adec7b5dd437dfc1603eb3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 11 Dec 2017 11:30:00 -0800 Subject: [PATCH 101/137] remove bullet stats when physics stats disabled --- interface/src/Menu.cpp | 3 ++- interface/src/ui/Stats.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9bbb72357b..2d15134b70 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -645,7 +645,8 @@ Menu::Menu() { // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false, + qApp, SLOT(enablePerfStats(bool))); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e194551add..f991420fe8 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -78,6 +78,8 @@ bool Stats::includeTimingRecord(const QString& name) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); } else if (name.startsWith("/paintGL/")) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); + } else if (name.startsWith("step/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming); } return true; } From 58fa8b9fbfb3463a6bb2b588ec63d7e4810529fa Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 12 Dec 2017 10:49:05 +1300 Subject: [PATCH 102/137] Miscellaneous fixes --- libraries/shared/src/PointerEvent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index e989fa6ad0..c07de18032 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -72,8 +72,8 @@ void PointerEvent::setButton(Button button) { * "Move". * @property {number} id - Integer number used to identify the pointer: 0 = hardware mouse, 1 = left * controller, 2 = right controller. - * @property {Vec2} pos2D - The 2D position of the event on the intersected overlay or entity XY plane. - * @property {Vec3} pos3D - The 3D position of the event on the intersected overlay or entity. + * @property {Vec2} pos2D - The 2D position of the event on the intersected overlay or entity XY plane, where applicable. + * @property {Vec3} pos3D - The 3D position of the event on the intersected overlay or entity, where applicable. * @property {Vec3} normal - The surface normal at the intersection point. * @property {Vec3} direction - The direction of the intersection ray. * @property {string} button - The name of the button pressed: None, Primary, Secondary, @@ -96,8 +96,8 @@ void PointerEvent::setButton(Button button) { * false * @property {boolean} isTertiaryHeld - true if the tertiary button is currently being pressed, otherwise * false - * @property {KeyboardModifiers} keyboardModifiers - Integer value being and with bits set according to which keyboard modifier - * keys are were pressed when the event was generated. + * @property {KeyboardModifiers} keyboardModifiers - Integer value with bits set according to which keyboard modifier keys were + * pressed when the event was generated. */ /**jsdoc *

A KeyboardModifiers value is used to specify which modifier keys on the keyboard are pressed. The value is the sum From ae625306ff6050f4ba0007fbbeabce15526e0302 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Dec 2017 14:54:29 -0800 Subject: [PATCH 103/137] remove getDedicatedMemory --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 -- libraries/gpu-gl/src/gpu/gl/GLShared.cpp | 47 ----------------------- libraries/gpu-gl/src/gpu/gl/GLShared.h | 1 - 3 files changed, 52 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index f138244fa2..6fb0d7b152 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -40,10 +40,6 @@ static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains( static GLBackend* INSTANCE{ nullptr }; BackendPointer GLBackend::createBackend() { - // The ATI memory info extension only exposes 'free memory' so we want to force it to - // cache the value as early as possible - getDedicatedMemory(); - // FIXME provide a mechanism to override the backend for testing // Where the gpuContext is initialized and where the TRUE Backend is created and assigned auto version = QOpenGLContextWrapper::currentContextVersion(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp index 7f46a0aa2b..5a015cd550 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp @@ -88,53 +88,6 @@ gpu::Size getFreeDedicatedMemory() { return result; } -gpu::Size getDedicatedMemory() { - static Size dedicatedMemory { 0 }; - static std::once_flag once; - std::call_once(once, [&] { -#ifdef Q_OS_WIN - if (!dedicatedMemory && wglGetGPUIDsAMD && wglGetGPUInfoAMD) { - UINT maxCount = wglGetGPUIDsAMD(0, 0); - std::vector ids; - ids.resize(maxCount); - wglGetGPUIDsAMD(maxCount, &ids[0]); - GLuint memTotal; - wglGetGPUInfoAMD(ids[0], WGL_GPU_RAM_AMD, GL_UNSIGNED_INT, sizeof(GLuint), &memTotal); - dedicatedMemory = MB_TO_BYTES(memTotal); - } -#endif - - if (!dedicatedMemory) { - GLint atiGpuMemory[4]; - // not really total memory, but close enough if called early enough in the application lifecycle - glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]); - } - } - - if (!dedicatedMemory) { - GLint nvGpuMemory { 0 }; - glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory); - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(nvGpuMemory); - } - } - - if (!dedicatedMemory) { - auto gpuIdent = GPUIdent::getInstance(); - if (gpuIdent && gpuIdent->isValid()) { - dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory()); - } - } - }); - - return dedicatedMemory; -} - - - - ComparisonFunction comparisonFuncFromGL(GLenum func) { if (func == GL_NEVER) { return NEVER; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index a1cf27afa6..d3ad7c028b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -28,7 +28,6 @@ void serverWait(); // Create a fence and synchronously wait on the fence void clientWait(); -gpu::Size getDedicatedMemory(); gpu::Size getFreeDedicatedMemory(); ComparisonFunction comparisonFuncFromGL(GLenum func); State::StencilOp stencilOpFromGL(GLenum stencilOp); From ce96fa70788bbce962f4e2d53bfc9ad7f00f2bfc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Dec 2017 17:32:58 -0800 Subject: [PATCH 104/137] change hysteresis values based on testing --- interface/src/raypick/StylusPointer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 21d257048c..5afbc2058a 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -23,10 +23,10 @@ static const float WEB_STYLUS_LENGTH = 0.2f; static const float TABLET_MIN_HOVER_DISTANCE = -0.1f; static const float TABLET_MAX_HOVER_DISTANCE = 0.1f; static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f; -static const float TABLET_MAX_TOUCH_DISTANCE = 0.01f; +static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f; static const float HOVER_HYSTERESIS = 0.01f; -static const float TOUCH_HYSTERESIS = 0.02f; +static const float TOUCH_HYSTERESIS = 0.001f; static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND; static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f; From 43279066304e8d38984b6101449df3d6a99b2f6a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 12 Dec 2017 16:39:49 +1300 Subject: [PATCH 105/137] Reduce communications load when making a user connection --- scripts/system/makeUserConnection.js | 94 ++++++++++++++++++---------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 7dddd46562..230a3d511b 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -111,10 +111,20 @@ var connectingHandJointIndex = -1; var waitingList = {}; var particleEffect; - var particleRotationAngle = 0.0; + var particleEmitRate; + var INITIAL_PARTICLE_EMIT_RATE = 500; + var MINIMUM_PARTICLE_EMIT_RATE = 50; + var PARTICLE_DECAY_RATE = 0.5; + var particleEffectUpdateTimer = null; + var PARTICLE_EFFECT_UPDATE_INTERVAL = 50; var makingConnectionParticleEffect; - var makingConnectionEmitRate = 2000; - var particleEmitRate = 500; + var makingConnectionEmitRate; + var isMakingConnectionEmitting; + var INITIAL_MAKING_CONNECTION_EMIT_RATE = 1000; + var MINIMUM_MAKING_CONNECTION_EMIT_RATE = 20; + var MAKING_CONNECTION_DECAY_RATE = 0.5; + var makingConnectionUpdateTimer = null; + var MAKING_CONNECTION_UPDATE_INTERVAL = 50; var handshakeInjector; var successfulHandshakeInjector; var handshakeSound; @@ -239,12 +249,20 @@ } function deleteParticleEffect() { + if (particleEffectUpdateTimer) { + Script.clearTimeout(particleEffectUpdateTimer); + particleEffectUpdateTimer = null; + } if (particleEffect) { particleEffect = Entities.deleteEntity(particleEffect); } } function deleteMakeConnectionParticleEffect() { + if (makingConnectionUpdateTimer) { + Script.clearTimeout(makingConnectionUpdateTimer); + makingConnectionUpdateTimer = null; + } if (makingConnectionParticleEffect) { makingConnectionParticleEffect = Entities.deleteEntity(makingConnectionParticleEffect); } @@ -257,15 +275,31 @@ } } - function calcParticlePos(myHandPosition, otherHandPosition, otherOrientation, reset) { - if (reset) { - particleRotationAngle = 0.0; + function updateMakingConnection() { + makingConnectionEmitRate = Math.max(makingConnectionEmitRate * MAKING_CONNECTION_DECAY_RATE, + MINIMUM_MAKING_CONNECTION_EMIT_RATE); + isMakingConnectionEmitting = true; + Entities.editEntity(makingConnectionParticleEffect, { + emitRate: makingConnectionEmitRate, + isEmitting: true + }); + if (makingConnectionEmitRate > MINIMUM_MAKING_CONNECTION_EMIT_RATE) { + makingConnectionUpdateTimer = Script.setTimeout(makingConnectionUpdateTimer, MAKING_CONNECTION_UPDATE_INTERVAL); + } else { + makingConnectionUpdateTimer = null; + } + } + + function updateParticleEffect() { + particleEmitRate = Math.max(MINIMUM_PARTICLE_EMIT_RATE, particleEmitRate * PARTICLE_DECAY_RATE); + Entities.editEntity(particleEffect, { + emitRate: particleEmitRate + }); + if (particleEmitRate > MINIMUM_PARTICLE_EMIT_RATE) { + particleEffectUpdateTimer = Script.setTimeout(updateParticleEffect, PARTICLE_EFFECT_UPDATE_INTERVAL); + } else { + particleEffectUpdateTimer = null; } - var position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); - particleRotationAngle += PARTICLE_ANGLE_INCREMENT; // about 0.5 hz - var radius = Math.min(PARTICLE_RADIUS, PARTICLE_RADIUS * particleRotationAngle / 360); - var axis = Vec3.mix(Quat.getFront(MyAvatar.orientation), Quat.inverse(Quat.getFront(otherOrientation)), 0.5); - return Vec3.sum(position, Vec3.multiplyQbyV(Quat.angleAxis(particleRotationAngle, axis), {x: 0, y: radius, z: 0})); } // this is called frequently, but usually does nothing @@ -301,41 +335,35 @@ positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); // now manage the rest of the entity if (!particleEffect) { - particleRotationAngle = 0.0; - particleEmitRate = 500; + particleEmitRate = INITIAL_PARTICLE_EMIT_RATE; particleProps = PARTICLE_EFFECT_PROPS; - particleProps.isEmitting = 0; - particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); + particleProps.position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); particleProps.parentID = MyAvatar.sessionUUID; particleEffect = Entities.addEntity(particleProps, true); - } else { - particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); - particleProps.isEmitting = 1; - Entities.editEntity(particleEffect, particleProps); } if (!makingConnectionParticleEffect) { var props = MAKING_CONNECTION_PARTICLE_PROPS; props.parentID = MyAvatar.sessionUUID; - makingConnectionEmitRate = 2000; + makingConnectionEmitRate = INITIAL_MAKING_CONNECTION_EMIT_RATE; props.emitRate = makingConnectionEmitRate; + props.isEmitting = false; props.position = myHandPosition; makingConnectionParticleEffect = Entities.addEntity(props, true); - } else { - makingConnectionEmitRate *= 0.5; - Entities.editEntity(makingConnectionParticleEffect, { - emitRate: makingConnectionEmitRate, - position: myHandPosition, - isEmitting: true - }); + makingConnectionUpdateTimer = Script.setTimeout(updateMakingConnection, MAKING_CONNECTION_UPDATE_INTERVAL); } break; case STATES.MAKING_CONNECTION: - particleEmitRate = Math.max(50, particleEmitRate * 0.5); - Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); - Entities.editEntity(particleEffect, { - position: calcParticlePos(myHandPosition, otherHandPosition, otherOrientation), - emitRate: particleEmitRate - }); + if (makingConnectionUpdateTimer) { + Script.clearTimeout(makingConnectionUpdateTimer); + makingConnectionUpdateTimer = null; + } + if (isMakingConnectionEmitting) { + Entities.editEntity(makingConnectionParticleEffect, { isEmitting: false }); + isMakingConnectionEmitting = false; + } + if (!particleEffectUpdateTimer && particleEmitRate > MINIMUM_PARTICLE_EMIT_RATE) { + particleEffectUpdateTimer = Script.setTimeout(updateParticleEffect, PARTICLE_EFFECT_UPDATE_INTERVAL); + } break; default: debug("unexpected state", state); From 380cd7aac6d322ca49171b02a173e019eea0321f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 12 Dec 2017 17:35:37 +1300 Subject: [PATCH 106/137] Tune particle effects to be similar to before --- scripts/system/makeUserConnection.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 230a3d511b..604ed65951 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -46,7 +46,7 @@ var PARTICLE_EFFECT_PROPS = { "alpha": 0.8, "azimuthFinish": Math.PI, - "azimuthStart": -1 * Math.PI, + "azimuthStart": -Math.PI, "emitRate": 500, "emitSpeed": 0.0, "emitterShouldTrail": 1, @@ -54,17 +54,19 @@ "lifespan": 3, "maxParticles": 1000, "particleRadius": 0.003, - "polarStart": 1, - "polarFinish": 1, + "polarStart": Math.PI / 2, + "polarFinish": Math.PI / 2, "radiusFinish": 0.008, "radiusStart": 0.0025, - "speedSpread": 0.025, + "emitSpeed": 0.02, + "speedSpread": 0.015, "textures": "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle.png", "color": {"red": 255, "green": 255, "blue": 255}, "colorFinish": {"red": 0, "green": 164, "blue": 255}, "colorStart": {"red": 255, "green": 255, "blue": 255}, "emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71}, "emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0}, + "emitDimensions": { "x": 0.15, "y": 0.15, "z": 0.01 }, "accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0}, "dimensions": {"x": 0.05, "y": 0.05, "z": 0.05}, "type": "ParticleEffect" @@ -112,19 +114,19 @@ var waitingList = {}; var particleEffect; var particleEmitRate; - var INITIAL_PARTICLE_EMIT_RATE = 500; + var INITIAL_PARTICLE_EMIT_RATE = 250; var MINIMUM_PARTICLE_EMIT_RATE = 50; var PARTICLE_DECAY_RATE = 0.5; var particleEffectUpdateTimer = null; - var PARTICLE_EFFECT_UPDATE_INTERVAL = 50; + var PARTICLE_EFFECT_UPDATE_INTERVAL = 200; var makingConnectionParticleEffect; var makingConnectionEmitRate; var isMakingConnectionEmitting; - var INITIAL_MAKING_CONNECTION_EMIT_RATE = 1000; + var INITIAL_MAKING_CONNECTION_EMIT_RATE = 500; var MINIMUM_MAKING_CONNECTION_EMIT_RATE = 20; var MAKING_CONNECTION_DECAY_RATE = 0.5; var makingConnectionUpdateTimer = null; - var MAKING_CONNECTION_UPDATE_INTERVAL = 50; + var MAKING_CONNECTION_UPDATE_INTERVAL = 200; var handshakeInjector; var successfulHandshakeInjector; var handshakeSound; @@ -338,6 +340,8 @@ particleEmitRate = INITIAL_PARTICLE_EMIT_RATE; particleProps = PARTICLE_EFFECT_PROPS; particleProps.position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); + particleProps.rotation = Vec3.mix(Quat.getFront(MyAvatar.orientation), + Quat.inverse(Quat.getFront(otherOrientation)), 0.5); particleProps.parentID = MyAvatar.sessionUUID; particleEffect = Entities.addEntity(particleProps, true); } From 29538851b61c17a8e8561dd1534942e535f40589 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 11 Dec 2017 18:50:07 -0800 Subject: [PATCH 107/137] Made the changes from the latest code review, except getting rid of the while loop. --- .../src/RenderableModelEntityItem.cpp | 4 +-- .../src/RenderableModelEntityItem.h | 1 - .../entities/src/AnimationPropertyGroup.cpp | 9 +++--- libraries/entities/src/ModelEntityItem.cpp | 31 ++++++++++--------- libraries/entities/src/ModelEntityItem.h | 4 +-- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4686cc94bc..e578e4858d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -992,7 +992,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - { + { // the current frame is set on the server in update() in ModelEntityItem.cpp int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); @@ -1313,7 +1313,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } // The code to deal with the change of properties is now in ModelEntityItem.cpp - // That is where _currentFrame and _lastAnimated are updated. + // That is where _currentFrame and _lastAnimated were updated. if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 44ee82713d..b4f2665692 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -184,7 +184,6 @@ private: bool _shouldHighlight { false }; bool _animating { false }; uint64_t _lastAnimated { 0 }; - float _currentFrame { -1 }; }; } } // namespace diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 932bdbf8c0..2af56fb6b2 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -22,24 +22,25 @@ const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f; bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return - (a._url == b._url) && + (a._currentFrame == b._currentFrame) && (a._running == b._running) && (a._loop == b._loop) && + (a._hold == b._hold) && (a._firstFrame == b._firstFrame) && (a._lastFrame == b._lastFrame) && - (a._hold == b._hold); + (a._url == b._url); } bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return - (a._url != b._url) || (a._currentFrame != b._currentFrame) || (a._running != b._running) || (a._loop != b._loop) || + (a._hold != b._hold) || (a._firstFrame != b._firstFrame) || (a._lastFrame != b._lastFrame) || - (a._hold != b._hold); + (a._url != b._url); } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 260074da68..4ade724a82 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -207,11 +207,16 @@ void ModelEntityItem::update(const quint64& now) { // don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set _currentFrame = currentAnimationProperties.getCurrentFrame(); setAnimationCurrentFrame(_currentFrame); + qCDebug(entities) << "setting first frame 1 " << _currentFrame; } else { - _lastAnimated = usecTimestampNow(); + _lastAnimated = usecTimestampNow(); _currentFrame = currentAnimationProperties.getFirstFrame(); setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); + qCDebug(entities) << "setting first frame 2" << _currentFrame; } + } else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) { + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(_currentFrame); } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { // don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated _currentFrame = currentAnimationProperties.getCurrentFrame(); @@ -228,13 +233,15 @@ void ModelEntityItem::update(const quint64& now) { // if the current frame is less than zero then we have restarted the server. if (_currentFrame < 0) { + //qCDebug(entities) << "setting first frame 3 " << _currentFrame; if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { - _currentFrame = currentAnimationProperties.getCurrentFrame(); + // _currentFrame = currentAnimationProperties.getCurrentFrame(); } else { - _currentFrame = currentAnimationProperties.getFirstFrame(); - setAnimationCurrentFrame(_currentFrame); - _lastAnimated = usecTimestampNow(); + //qCDebug(entities) << "setting first frame 4 " << _currentFrame; + // _currentFrame = currentAnimationProperties.getFirstFrame(); + // setAnimationCurrentFrame(_currentFrame); + // _lastAnimated = usecTimestampNow(); } } } @@ -269,7 +276,7 @@ void ModelEntityItem::updateFrameCount() { _lastAnimated = now; // if fps is negative then increment timestamp and return. - if (getAnimationFPS() < 0.0) { + if (getAnimationFPS() < 0.0f) { return; } @@ -280,8 +287,9 @@ void ModelEntityItem::updateFrameCount() { _currentFrame += (deltaTime * getAnimationFPS()); if (_currentFrame > getAnimationLastFrame()) { if (getAnimationLoop()) { - while ((_currentFrame - getAnimationFirstFrame()) > (updatedFrameCount - 1)) { - _currentFrame -= (updatedFrameCount - 1); + //_currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); + while (_currentFrame > (getAnimationFirstFrame() + (updatedFrameCount - 1))) { + _currentFrame = _currentFrame - (updatedFrameCount - 1); } } else { _currentFrame = getAnimationLastFrame(); @@ -293,6 +301,7 @@ void ModelEntityItem::updateFrameCount() { _currentFrame = getAnimationFirstFrame(); } } + qCDebug(entities) << "in update frame " << _currentFrame; setAnimationCurrentFrame(_currentFrame); } @@ -720,9 +729,3 @@ bool ModelEntityItem::isAnimatingSomething() const { (_animationProperties.getFPS() != 0.0f); }); } - -int ModelEntityItem::getLastKnownCurrentFrame() const { - return resultWithReadLock([&] { - return _lastKnownCurrentFrame; - }); -} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index c0ca12c7f9..7fee022011 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -110,8 +110,6 @@ public: float getAnimationFPS() const; bool isAnimatingSomething() const; - int getLastKnownCurrentFrame() const; - static const QString DEFAULT_TEXTURES; const QString getTextures() const; void setTextures(const QString& textures); @@ -172,7 +170,7 @@ protected: private: uint64_t _lastAnimated{ 0 }; AnimationPropertyGroup _previousAnimationProperties; - float _currentFrame{ -1 }; + float _currentFrame{ -1.0f }; }; #endif // hifi_ModelEntityItem_h From 079d9639e477844bdb79f213cafc21334f15f806 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 12 Dec 2017 09:12:11 -0800 Subject: [PATCH 108/137] Got rid of the while loop in updateFrameCount in ModelEntityItem_cpp --- libraries/entities/src/ModelEntityItem.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4ade724a82..3215ab9dd0 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -287,10 +287,7 @@ void ModelEntityItem::updateFrameCount() { _currentFrame += (deltaTime * getAnimationFPS()); if (_currentFrame > getAnimationLastFrame()) { if (getAnimationLoop()) { - //_currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); - while (_currentFrame > (getAnimationFirstFrame() + (updatedFrameCount - 1))) { - _currentFrame = _currentFrame - (updatedFrameCount - 1); - } + _currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); } else { _currentFrame = getAnimationLastFrame(); } From 25214aad2368f2e9099fb5621fa1990abd2b281c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 13 Dec 2017 08:56:36 +1300 Subject: [PATCH 109/137] Code review --- scripts/system/makeUserConnection.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 604ed65951..392ff3c0b5 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -48,7 +48,6 @@ "azimuthFinish": Math.PI, "azimuthStart": -Math.PI, "emitRate": 500, - "emitSpeed": 0.0, "emitterShouldTrail": 1, "isEmitting": 1, "lifespan": 3, @@ -114,16 +113,16 @@ var waitingList = {}; var particleEffect; var particleEmitRate; - var INITIAL_PARTICLE_EMIT_RATE = 250; - var MINIMUM_PARTICLE_EMIT_RATE = 50; + var PARTICLE_INITIAL_EMIT_RATE = 250; + var PARTICLE_MINIMUM_EMIT_RATE = 50; var PARTICLE_DECAY_RATE = 0.5; var particleEffectUpdateTimer = null; var PARTICLE_EFFECT_UPDATE_INTERVAL = 200; var makingConnectionParticleEffect; var makingConnectionEmitRate; var isMakingConnectionEmitting; - var INITIAL_MAKING_CONNECTION_EMIT_RATE = 500; - var MINIMUM_MAKING_CONNECTION_EMIT_RATE = 20; + var MAKING_CONNECTION_INITIAL_EMIT_RATE = 500; + var MAKING_CONNECTION_MINIMUM_EMIT_RATE = 20; var MAKING_CONNECTION_DECAY_RATE = 0.5; var makingConnectionUpdateTimer = null; var MAKING_CONNECTION_UPDATE_INTERVAL = 200; @@ -279,13 +278,13 @@ function updateMakingConnection() { makingConnectionEmitRate = Math.max(makingConnectionEmitRate * MAKING_CONNECTION_DECAY_RATE, - MINIMUM_MAKING_CONNECTION_EMIT_RATE); + MAKING_CONNECTION_MINIMUM_EMIT_RATE); isMakingConnectionEmitting = true; Entities.editEntity(makingConnectionParticleEffect, { emitRate: makingConnectionEmitRate, isEmitting: true }); - if (makingConnectionEmitRate > MINIMUM_MAKING_CONNECTION_EMIT_RATE) { + if (makingConnectionEmitRate > MAKING_CONNECTION_MINIMUM_EMIT_RATE) { makingConnectionUpdateTimer = Script.setTimeout(makingConnectionUpdateTimer, MAKING_CONNECTION_UPDATE_INTERVAL); } else { makingConnectionUpdateTimer = null; @@ -293,11 +292,11 @@ } function updateParticleEffect() { - particleEmitRate = Math.max(MINIMUM_PARTICLE_EMIT_RATE, particleEmitRate * PARTICLE_DECAY_RATE); + particleEmitRate = Math.max(PARTICLE_MINIMUM_EMIT_RATE, particleEmitRate * PARTICLE_DECAY_RATE); Entities.editEntity(particleEffect, { emitRate: particleEmitRate }); - if (particleEmitRate > MINIMUM_PARTICLE_EMIT_RATE) { + if (particleEmitRate > PARTICLE_MINIMUM_EMIT_RATE) { particleEffectUpdateTimer = Script.setTimeout(updateParticleEffect, PARTICLE_EFFECT_UPDATE_INTERVAL); } else { particleEffectUpdateTimer = null; @@ -337,7 +336,7 @@ positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); // now manage the rest of the entity if (!particleEffect) { - particleEmitRate = INITIAL_PARTICLE_EMIT_RATE; + particleEmitRate = PARTICLE_INITIAL_EMIT_RATE; particleProps = PARTICLE_EFFECT_PROPS; particleProps.position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); particleProps.rotation = Vec3.mix(Quat.getFront(MyAvatar.orientation), @@ -348,7 +347,7 @@ if (!makingConnectionParticleEffect) { var props = MAKING_CONNECTION_PARTICLE_PROPS; props.parentID = MyAvatar.sessionUUID; - makingConnectionEmitRate = INITIAL_MAKING_CONNECTION_EMIT_RATE; + makingConnectionEmitRate = MAKING_CONNECTION_INITIAL_EMIT_RATE; props.emitRate = makingConnectionEmitRate; props.isEmitting = false; props.position = myHandPosition; @@ -365,7 +364,7 @@ Entities.editEntity(makingConnectionParticleEffect, { isEmitting: false }); isMakingConnectionEmitting = false; } - if (!particleEffectUpdateTimer && particleEmitRate > MINIMUM_PARTICLE_EMIT_RATE) { + if (!particleEffectUpdateTimer && particleEmitRate > PARTICLE_MINIMUM_EMIT_RATE) { particleEffectUpdateTimer = Script.setTimeout(updateParticleEffect, PARTICLE_EFFECT_UPDATE_INTERVAL); } break; From dba05ae8e2039e7c568cbb7f56cdf083e9dbff7c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Dec 2017 12:38:01 -0800 Subject: [PATCH 110/137] try harder to keep setTimeout loop going in controller-dispatcher --- scripts/system/controllers/controllerDispatcher.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 57f3b4fd8b..af3b1d2ed6 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -154,6 +154,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.update = function () { + try { + this.updateInternal(); + } catch (e) { + print(e); + } + Script.setTimeout(_this.update, BASIC_TIMER_INTERVAL_MS); + }; + + this.updateInternal = function () { if (PROFILE) { Script.beginProfileRange("dispatch.pre"); } @@ -376,7 +385,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (PROFILE) { Script.endProfileRange("dispatch.run"); } - Script.setTimeout(_this.update, BASIC_TIMER_INTERVAL_MS); }; this.setBlacklist = function() { From 3a00d31c15c78f7b0d7e6fe74782573bb862a608 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Dec 2017 12:59:35 -0800 Subject: [PATCH 111/137] oops --- scripts/system/controllers/controllerDispatcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index af3b1d2ed6..915b3b3680 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -155,7 +155,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.update = function () { try { - this.updateInternal(); + _this.updateInternal(); } catch (e) { print(e); } From 623e6395be15677d2394587489e36ed58020de17 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 12 Dec 2017 12:09:40 -0800 Subject: [PATCH 112/137] Set Wallet Status to 'Ready' after correctly authenticating wallet --- interface/src/commerce/Wallet.cpp | 8 +++----- interface/src/scripting/WalletScriptingInterface.cpp | 5 +++++ interface/src/scripting/WalletScriptingInterface.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 69914e97a4..00941d6c50 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -315,7 +315,6 @@ Wallet::Wallet() { } walletScriptingInterface->setWalletStatus(status); - emit walletStatusResult(status); }); auto accountManager = DependencyManager::get(); @@ -491,6 +490,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { } if (_publicKeys.count() > 0) { // we _must_ be authenticated if the publicKeys are there + DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); return true; } @@ -503,6 +503,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // be sure to add the public key so we don't do this over and over _publicKeys.push_back(publicKey.toBase64()); + DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); return true; } } @@ -801,15 +802,12 @@ void Wallet::account() { void Wallet::getWalletStatus() { auto walletScriptingInterface = DependencyManager::get(); - uint status; if (DependencyManager::get()->isLoggedIn()) { // This will set account info for the wallet, allowing us to decrypt and display the security image. account(); } else { - status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN; - emit walletStatusResult(status); - walletScriptingInterface->setWalletStatus(status); + walletScriptingInterface->setWalletStatus((uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN); return; } } diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 8b4279af02..71a7076bdf 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -22,3 +22,8 @@ void WalletScriptingInterface::refreshWalletStatus() { auto wallet = DependencyManager::get(); wallet->getWalletStatus(); } + +void WalletScriptingInterface::setWalletStatus(const uint& status) { + _walletStatus = status; + emit DependencyManager::get()->walletStatusResult(status); +} \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index d7f9d9242e..a9948a68c6 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -39,7 +39,7 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } - void setWalletStatus(const uint& status) { _walletStatus = status; } + void setWalletStatus(const uint& status); signals: void walletStatusChanged(); From 16f545a9e8d8900549ff2ef958d50f3a8d63a19b Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 12 Dec 2017 11:41:49 -0800 Subject: [PATCH 113/137] fix circle3d overlays --- interface/src/ui/overlays/Base3DOverlay.h | 4 +- interface/src/ui/overlays/Circle3DOverlay.cpp | 54 +++++++++++++------ interface/src/ui/overlays/Circle3DOverlay.h | 20 +++---- interface/src/ui/overlays/Overlay.cpp | 4 +- interface/src/ui/overlays/Overlay.h | 3 ++ interface/src/ui/overlays/Planar3DOverlay.h | 4 +- 6 files changed, 57 insertions(+), 32 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 6323ff9dc8..d65b9850d7 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -61,8 +61,8 @@ public: void notifyRenderVariableChange() const; - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; + virtual void setProperties(const QVariantMap& properties) override; + virtual QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal); diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index a8230d6bee..658e91865e 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -16,10 +16,7 @@ QString const Circle3DOverlay::TYPE = "circle3d"; -Circle3DOverlay::Circle3DOverlay() { - memset(&_minorTickMarksColor, 0, sizeof(_minorTickMarksColor)); - memset(&_majorTickMarksColor, 0, sizeof(_majorTickMarksColor)); -} +Circle3DOverlay::Circle3DOverlay() {} Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : Planar3DOverlay(circle3DOverlay), @@ -27,17 +24,21 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : _endAt(circle3DOverlay->_endAt), _outerRadius(circle3DOverlay->_outerRadius), _innerRadius(circle3DOverlay->_innerRadius), + _innerStartColor(circle3DOverlay->_innerStartColor), + _innerEndColor(circle3DOverlay->_innerEndColor), + _outerStartColor(circle3DOverlay->_outerStartColor), + _outerEndColor(circle3DOverlay->_outerEndColor), + _innerStartAlpha(circle3DOverlay->_innerStartAlpha), + _innerEndAlpha(circle3DOverlay->_innerEndAlpha), + _outerStartAlpha(circle3DOverlay->_outerStartAlpha), + _outerEndAlpha(circle3DOverlay->_outerEndAlpha), _hasTickMarks(circle3DOverlay->_hasTickMarks), _majorTickMarksAngle(circle3DOverlay->_majorTickMarksAngle), _minorTickMarksAngle(circle3DOverlay->_minorTickMarksAngle), _majorTickMarksLength(circle3DOverlay->_majorTickMarksLength), _minorTickMarksLength(circle3DOverlay->_minorTickMarksLength), _majorTickMarksColor(circle3DOverlay->_majorTickMarksColor), - _minorTickMarksColor(circle3DOverlay->_minorTickMarksColor), - _quadVerticesID(GeometryCache::UNKNOWN_ID), - _lineVerticesID(GeometryCache::UNKNOWN_ID), - _majorTicksVerticesID(GeometryCache::UNKNOWN_ID), - _minorTicksVerticesID(GeometryCache::UNKNOWN_ID) + _minorTickMarksColor(circle3DOverlay->_minorTickMarksColor) { } @@ -80,9 +81,8 @@ void Circle3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); auto& batch = *args->_batch; - if (args->_shapePipeline) { - batch.setPipeline(args->_shapePipeline->pipeline); - } + + DependencyManager::get()->bindSimpleProgram(batch, false, isTransparent(), false, !getIsSolid(), true); batch.setModelTransform(getRenderTransform()); @@ -185,11 +185,10 @@ void Circle3DOverlay::render(RenderArgs* args) { // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise // we just draw a line... if (getHasTickMarks()) { - - if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) { + if (!_majorTicksVerticesID) { _majorTicksVerticesID = geometryCache->allocateID(); } - if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) { + if (!_minorTicksVerticesID) { _minorTicksVerticesID = geometryCache->allocateID(); } @@ -384,6 +383,30 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { if (property == "innerRadius") { return _innerRadius; } + if (property == "innerStartColor") { + return xColorToVariant(_innerStartColor); + } + if (property == "innerEndColor") { + return xColorToVariant(_innerEndColor); + } + if (property == "outerStartColor") { + return xColorToVariant(_outerStartColor); + } + if (property == "outerEndColor") { + return xColorToVariant(_outerEndColor); + } + if (property == "innerStartAlpha") { + return _innerStartAlpha; + } + if (property == "innerEndAlpha") { + return _innerEndAlpha; + } + if (property == "outerStartAlpha") { + return _outerStartAlpha; + } + if (property == "outerEndAlpha") { + return _outerEndAlpha; + } if (property == "hasTickMarks") { return _hasTickMarks; } @@ -409,7 +432,6 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { return Planar3DOverlay::getProperty(property); } - bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index 11c9c9710f..ef491b7f46 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -65,22 +65,22 @@ protected: float _outerRadius { 1 }; float _innerRadius { 0 }; - xColor _innerStartColor; - xColor _innerEndColor; - xColor _outerStartColor; - xColor _outerEndColor; - float _innerStartAlpha; - float _innerEndAlpha; - float _outerStartAlpha; - float _outerEndAlpha; + xColor _innerStartColor { DEFAULT_OVERLAY_COLOR }; + xColor _innerEndColor { DEFAULT_OVERLAY_COLOR }; + xColor _outerStartColor { DEFAULT_OVERLAY_COLOR }; + xColor _outerEndColor { DEFAULT_OVERLAY_COLOR }; + float _innerStartAlpha { DEFAULT_ALPHA }; + float _innerEndAlpha { DEFAULT_ALPHA }; + float _outerStartAlpha { DEFAULT_ALPHA }; + float _outerEndAlpha { DEFAULT_ALPHA }; bool _hasTickMarks { false }; float _majorTickMarksAngle { 0 }; float _minorTickMarksAngle { 0 }; float _majorTickMarksLength { 0 }; float _minorTickMarksLength { 0 }; - xColor _majorTickMarksColor; - xColor _minorTickMarksColor; + xColor _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; + xColor _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN }; int _quadVerticesID { 0 }; int _lineVerticesID { 0 }; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 01ad56f20e..e6b1eb2ebf 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -15,8 +15,8 @@ #include "Application.h" -static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; -static const float DEFAULT_ALPHA = 0.7f; +const xColor Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; +const float Overlay::DEFAULT_ALPHA = 0.7f; Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 806fc1aa14..9b07f24600 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -122,6 +122,9 @@ protected: unsigned int _stackOrder { 0 }; + static const xColor DEFAULT_OVERLAY_COLOR; + static const float DEFAULT_ALPHA; + private: OverlayID _overlayID; // only used for non-3d overlays }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 1360cccfc2..0a0e75696e 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -27,8 +27,8 @@ public: void setDimensions(float value) { setDimensions(glm::vec2(value)); } void setDimensions(const glm::vec2& value); - void setProperties(const QVariantMap& properties) override; - QVariant getProperty(const QString& property) override; + virtual void setProperties(const QVariantMap& properties) override; + virtual QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; From 650e7e192224be9a63dfae104843565e09525b98 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 13 Dec 2017 10:34:33 +1300 Subject: [PATCH 114/137] Document that getProperty() only works for 3D overlays --- interface/src/ui/overlays/Overlays.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 1a42d5fb5c..abd657074c 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -258,11 +258,12 @@ public slots: OverlayID getOverlayAtPoint(const glm::vec2& point); /**jsdoc - * Get the value of an overlay's property. + * Get the value of a 3D overlay's property. * @function Overlays.getProperty - * @param {Uuid} overlayID - The ID of the overlay. + * @param {Uuid} overlayID - The ID of the overlay. Must be for a 3D {@link Overlays.OverlayType|OverlayType}. * @param {string} property - The name of the property value to get. - * @returns {object} The value of the property if the overlay and property can be found, otherwise undefined. + * @returns {object} The value of the property if the 3D overlay and property can be found, otherwise + * undefined. * @example Create an overlay in front of your avatar then report its alpha property value. * var overlay = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), From 87fa919ac8d2c38198c515df3e9dcdb49c32bc23 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Dec 2017 14:11:25 -0800 Subject: [PATCH 115/137] save files to Documents by default --- .../src/scripting/TestScriptingInterface.cpp | 14 ++++-- libraries/shared/src/Trace.cpp | 45 +++++-------------- libraries/shared/src/shared/FileUtils.cpp | 45 +++++++++++++++++-- libraries/shared/src/shared/FileUtils.h | 3 ++ 4 files changed, 66 insertions(+), 41 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index b53b9d5d5f..74a15db0ce 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -11,11 +11,12 @@ #include #include +#include #include #include -#include -#include #include +#include +#include #include "Application.h" @@ -141,8 +142,13 @@ void TestScriptingInterface::endTraceEvent(QString name) { tracing::traceEvent(trace_test(), name, tracing::DurationEnd); } -void TestScriptingInterface::savePhysicsSimulationStats(QString filename) { - qApp->saveNextPhysicsStats(filename); +void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) { + QString path = FileUtils::replaceDateTimeTokens(originalPath); + path = FileUtils::computeDocumentPath(path); + if (!FileUtils::canCreateFile(path)) { + return; + } + qApp->saveNextPhysicsStats(path); } void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) { diff --git a/libraries/shared/src/Trace.cpp b/libraries/shared/src/Trace.cpp index d7feb65ff3..3f6a2dd643 100644 --- a/libraries/shared/src/Trace.cpp +++ b/libraries/shared/src/Trace.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -31,6 +30,8 @@ #include "Gzip.h" #include "PortableHighResolutionClock.h" +#include "SharedLogging.h" +#include "shared/FileUtils.h" #include "shared/GlobalAppProperties.h" using namespace tracing; @@ -104,30 +105,13 @@ void TraceEvent::writeJson(QTextStream& out) const { #endif } -void Tracer::serialize(const QString& originalPath) { - - QString path = originalPath; - - // Filter for specific tokens potentially present in the path: - auto now = QDateTime::currentDateTime(); - - path = path.replace("{DATE}", now.date().toString("yyyyMMdd")); - path = path.replace("{TIME}", now.time().toString("HHmm")); - - // If the filename is relative, turn it into an absolute path relative to the document directory. - QFileInfo originalFileInfo(path); - if (originalFileInfo.isRelative()) { - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); - } +void Tracer::serialize(const QString& filename) { + QString fullPath = FileUtils::replaceDateTimeTokens(filename); + fullPath = FileUtils::computeDocumentPath(fullPath); + if (!FileUtils::canCreateFile(fullPath)) { + return; } - - std::list currentEvents; { std::lock_guard guard(_eventsMutex); @@ -137,11 +121,6 @@ void Tracer::serialize(const QString& originalPath) { } } - // If the file exists and we can't remove it, fail early - if (QFileInfo(path).exists() && !QFile::remove(path)) { - return; - } - // If we can't open a temp file for writing, fail early QByteArray data; { @@ -159,15 +138,16 @@ void Tracer::serialize(const QString& originalPath) { out << "\n]"; } - if (path.endsWith(".gz")) { + if (fullPath.endsWith(".gz")) { QByteArray compressed; gzip(data, compressed); data = compressed; - } - + } + { - QFile file(path); + QFile file(fullPath); if (!file.open(QIODevice::WriteOnly)) { + qDebug(shared) << "failed to open file '" << fullPath << "'"; return; } file.write(data); @@ -191,7 +171,6 @@ void Tracer::serialize(const QString& originalPath) { } } } }; - data = document.toJson(QJsonDocument::Compact); } #endif diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 8c962dfd6d..dba0af7b16 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -12,6 +12,7 @@ #include "FileUtils.h" +#include #include #include #include @@ -20,6 +21,8 @@ #include #include +#include "../SharedLogging.h" + QString FileUtils::readFile(const QString& filename) { QFile file(filename); @@ -82,20 +85,54 @@ QString FileUtils::standardPath(QString subfolder) { // standard path // Mac: ~/Library/Application Support/Interface QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - if (!subfolder.startsWith("/")) { subfolder.prepend("/"); } - if (!subfolder.endsWith("/")) { subfolder.append("/"); } - path.append(subfolder); QDir logDir(path); if (!logDir.exists(path)) { logDir.mkpath(path); } - return path; } + +QString FileUtils::replaceDateTimeTokens(const QString& originalPath) { + // Filter for specific tokens potentially present in the path: + auto now = QDateTime::currentDateTime(); + QString path = originalPath; + path.replace("{DATE}", now.date().toString("yyyyMMdd")); + path.replace("{TIME}", now.time().toString("HHmm")); + return path; +} + + +QString FileUtils::computeDocumentPath(const QString& originalPath) { + // If the filename is relative, turn it into an absolute path relative to the document directory. + QString path = originalPath; + QFileInfo originalFileInfo(originalPath); + if (originalFileInfo.isRelative()) { + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + originalPath; + } + return path; +} + +bool FileUtils::canCreateFile(const QString& fullPath) { + // If the file exists and we can't remove it, fail early + QFileInfo fileInfo(fullPath); + if (fileInfo.exists() && !QFile::remove(fullPath)) { + qDebug(shared) << "unable to overwrite file '" << fullPath << "'"; + return false; + } + QDir dir(fileInfo.absolutePath()); + if (!dir.exists()) { + if (!dir.mkpath(fullPath)) { + qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'"; + return false; + } + } + return true; +} diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index 4f2c1b7af5..d68fcd8a44 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -21,6 +21,9 @@ public: static QString standardPath(QString subfolder); static QString readFile(const QString& filename); static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts); + static QString replaceDateTimeTokens(const QString& path); + static QString computeDocumentPath(const QString& path); + static bool canCreateFile(const QString& fullPath); }; #endif // hifi_FileUtils_h From 92be33da315ba6fb063646ddde5cc55b84a1d4e1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 13 Dec 2017 11:40:44 +1300 Subject: [PATCH 116/137] Web3d overlay's "resolution" property has been removed --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index e332b58b41..71d1c4b842 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -518,10 +518,10 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * * @property {string} url - The URL of the Web page to display. * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page. - * @property {Vec2} resolution=640,480 - The number of pixels. Deprecated: This property is being removed. - * @property {number} dpi=30.47 - The dots per inch to display the Web page at. - * @property {Vec2} dimensions=1,1 - Scales the size of the overlay calculated from resolution and - * dpi. Synonyms: scale, size. + * @property {Vec2} resolution - Deprecated: This property has been removed. + * @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay. + * @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms: + * scale, size. * @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second. * @property {boolean} showKeyboardFocusHighlight=true - If true, the Web overlay is highlighted when it has * keyboard focus. From 2d21f4d364ef9fb767935b37d2a938862eab4f02 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 12 Dec 2017 16:00:17 -0700 Subject: [PATCH 117/137] Fix web entities accessing the webcam --- interface/resources/html/createGlobalEventBridge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index b85aa33e33..a180fbc6cc 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -34,7 +34,7 @@ var EventBridge; var tempEventBridge = EventBridge; EventBridge = channel.objects.eventBridge; EventBridge.audioOutputDeviceChanged.connect(function(deviceName) { - navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(mediaStream) { + navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(mediaStream) { navigator.mediaDevices.enumerateDevices().then(function(devices) { devices.forEach(function(device) { if (device.kind == "audiooutput") { From 139976d8bb68597967c1f4ab7cc945fb8073ab38 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Dec 2017 16:19:17 -0800 Subject: [PATCH 118/137] allow staging METAVERSE_SERVER_URL to be toggled by env --- assignment-client/src/entities/EntityServer.cpp | 2 +- domain-server/src/DomainServer.cpp | 6 +++--- ice-server/src/IceServer.cpp | 2 +- interface/src/Application.cpp | 2 +- interface/src/commerce/Ledger.cpp | 2 +- interface/src/networking/CloseEventSender.cpp | 2 +- interface/src/ui/AddressBarDialog.h | 2 +- .../src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- libraries/entities/src/EntityItem.cpp | 2 +- libraries/entities/src/EntityItemProperties.cpp | 1 - libraries/entities/src/EntityTree.cpp | 2 +- libraries/networking/src/AccountManager.h | 2 +- libraries/networking/src/NetworkingConstants.h | 17 +++++++++++++++-- .../src/OAuthNetworkAccessManager.cpp | 2 +- .../script-engine/src/XMLHttpRequestClass.cpp | 2 +- libraries/ui/src/ui/types/RequestFilters.cpp | 2 +- tools/ac-client/src/ACClientApp.cpp | 2 +- tools/atp-client/src/ATPClientApp.cpp | 2 +- 18 files changed, 34 insertions(+), 22 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 995a5bad27..ecdf14ebec 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -477,7 +477,7 @@ void EntityServer::startDynamicDomainVerification() { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); QJsonObject request; request["certificate_id"] = i.key(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c2fe3af7c1..2d8bf7418a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -94,7 +94,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection, root.insert(requestSubobjectKey, subobject); QJsonDocument doc { root }; - QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath }; + QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + metaversePath }; QNetworkRequest req(url); req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -420,7 +420,7 @@ bool DomainServer::optionallySetupOAuth() { // if we don't have an oauth provider URL then we default to the default node auth url if (_oauthProviderURL.isEmpty()) { - _oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL; + _oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL(); } auto accountManager = DependencyManager::get(); @@ -2159,7 +2159,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url QJsonDocument doc(root); - QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id }; + QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id }; url.setQuery("access_token=" + accessTokenVariant->toString()); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index f8bf1f62ae..3cf1e1450e 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -208,7 +208,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) { // send a request to the metaverse API for the public key for this domain auto& networkAccessManager = NetworkAccessManager::getInstance(); - QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL }; + QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL() }; QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID)); publicKeyURL.setPath(publicKeyPath); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0176acf108..3e4ab16908 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -971,7 +971,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the account manager's root URL and trigger a login request if we don't have the access token accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto addressManager = DependencyManager::get(); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index b0d293584c..3257a634c7 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -130,7 +130,7 @@ QString amountString(const QString& label, const QString&color, const QJsonValue return result + QString(""); } -static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/"; +static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void Ledger::historySuccess(QNetworkReply& reply) { // here we send a historyResult with some extra stuff in it // Namely, the styled text we'd like to show. The issue is the diff --git a/interface/src/networking/CloseEventSender.cpp b/interface/src/networking/CloseEventSender.cpp index de8bd897b2..fe939afe05 100644 --- a/interface/src/networking/CloseEventSender.cpp +++ b/interface/src/networking/CloseEventSender.cpp @@ -28,7 +28,7 @@ QNetworkRequest createNetworkRequest() { QNetworkRequest request; - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath(USER_ACTIVITY_URL); request.setUrl(requestURL); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index cab533cce3..66f208ca90 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -30,7 +30,7 @@ public: bool forwardEnabled() { return _forwardEnabled; } bool useFeed() { return _useFeed; } void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } } - QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); } + QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL().toString(); } signals: void backEnabledChanged(); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index de644f165b..c41663fcc9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -289,7 +289,7 @@ void ContextOverlayInterface::openInspectionCertificate() { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; request["certificate_id"] = entityProperties.getCertificateID(); @@ -359,7 +359,7 @@ void ContextOverlayInterface::openInspectionCertificate() { } } -static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/"; +static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 48370b02fd..503bdd8349 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2840,7 +2840,7 @@ void EntityItem::retrieveMarketplacePublicKey() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/marketplace_key"); QJsonObject request; networkRequest.setUrl(requestURL); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 9f7ba1cc80..c7b08f2110 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e62399ce95..0d91f3787e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1308,7 +1308,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; request["certificate_id"] = certID; diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 7d97687d0b..87b17d00d5 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -97,7 +97,7 @@ public: void setTemporaryDomain(const QUuid& domainID, const QString& key); const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); } - QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL; } + QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); } public slots: void requestAccessToken(const QString& login, const QString& password); diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 0c210e4360..20d98563c5 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -12,15 +12,28 @@ #ifndef hifi_NetworkingConstants_h #define hifi_NetworkingConstants_h +#include #include namespace NetworkingConstants { // If you want to use STAGING instead of STABLE, - // don't forget to ALSO change the Domain Server Metaverse Server URL inside of: + // links from the Domain Server web interface (like the connect account token generation) + // will still point at stable unless you ALSO change the Domain Server Metaverse Server URL inside of: // \domain-server\resources\web\js\shared.js + + // You can avoid changing that and still effectively use a connected domain on staging + // if you manually generate a personal access token for the domains scope + // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true + const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); - const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE; + + static const QUrl METAVERSE_SERVER_URL() { + static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains("HIFI_STAGING_METAVERSE") + ? METAVERSE_SERVER_URL_STAGING + : METAVERSE_SERVER_URL_STABLE; + return serverURL; + }; } #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp index 15d5acbc67..a30786efa4 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.cpp +++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp @@ -35,7 +35,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O auto accountManager = DependencyManager::get(); if (accountManager->hasValidAccessToken() - && req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL.host()) { + && req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL().host()) { QNetworkRequest authenticatedRequest(req); authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 1d3c8fda32..62384f9d97 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -22,7 +22,7 @@ #include "ScriptEngine.h" #include "XMLHttpRequestClass.h" -const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/"; +const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/"; Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 0a0e67756d..d8658f1c14 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -20,7 +20,7 @@ namespace { bool isAuthableHighFidelityURL(const QUrl& url) { - auto metaverseServerURL = NetworkingConstants::METAVERSE_SERVER_URL; + auto metaverseServerURL = NetworkingConstants::METAVERSE_SERVER_URL(); static const QStringList HF_HOSTS = { "highfidelity.com", "highfidelity.io", metaverseServerURL.toString(), "metaverse.highfidelity.io" diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index e00560158f..88884a4fee 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -106,7 +106,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : auto accountManager = DependencyManager::get(); accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto nodeList = DependencyManager::get(); diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index c5edf27b67..9fd1bf8d4f 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -145,7 +145,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : auto accountManager = DependencyManager::get(); accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto nodeList = DependencyManager::get(); From 7a81c3e8e785d775ac736310daff443eeed52722 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Dec 2017 16:32:43 -0800 Subject: [PATCH 119/137] allow passing of custom URL on env --- libraries/networking/src/NetworkingConstants.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 20d98563c5..a4726f9b1a 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -28,9 +28,12 @@ namespace NetworkingConstants { const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); + // You can change the return of this function if you want to use a custom metaverse URL at compile time + // or you can pass a custom URL via the env variable static const QUrl METAVERSE_SERVER_URL() { - static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains("HIFI_STAGING_METAVERSE") - ? METAVERSE_SERVER_URL_STAGING + static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; + static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) + ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) : METAVERSE_SERVER_URL_STABLE; return serverURL; }; From 19e240460dca02c194cc7c3d8d4844ea7c885a4a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 13 Dec 2017 15:30:57 +0100 Subject: [PATCH 120/137] Slightly optimized matrix operations in LightStage as samcake's remark --- libraries/render-utils/src/LightStage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index b9b774452c..e568554452 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -185,7 +185,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, } // Update the buffer auto& schema = _schemaBuffer.edit(); - schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); + schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f); } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, From c4a7f4843a03d0ac65d9321879c5afe35d56c3dd Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 12 Dec 2017 17:02:07 -0800 Subject: [PATCH 121/137] Much more stable hips IK target. Target is now estimated in sensor space from the head, which is very deterministic and not prone to feedback from the IK system. Previous the hip was estimated from accumulated IK error deltas, which was not stable, as the error would tend to accumulate if the IK targets could not be reached. --- interface/src/avatar/MySkeletonModel.cpp | 69 ++++++++++++++++++++++++ interface/src/avatar/MySkeletonModel.h | 3 ++ 2 files changed, 72 insertions(+) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index a707031167..f249be33ea 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -9,9 +9,12 @@ #include "MySkeletonModel.h" #include +#include #include "Application.h" #include "InterfaceLogging.h" +#include "AnimUtil.h" + MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) { } @@ -30,6 +33,39 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle }; } +static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { + 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 + const float MIX_RATIO = 0.5f; + hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO); + + if (isFlying) { + // rotate the hips back to match the flying animation. + + const float TILT_ANGLE = 0.523f; + const glm::quat tiltRot = glm::angleAxis(TILT_ANGLE, transformVectorFast(avatarToSensorMat, -Vectors::UNIT_X)); + + glm::vec3 headPos; + int headIndex = myAvatar->getJointIndex("Head"); + if (headIndex != -1) { + headPos = transformPoint(avatarToSensorMat, myAvatar->getAbsoluteJointTranslationInObjectFrame(headIndex)); + } else { + headPos = transformPoint(myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition()); + } + hipsRot = tiltRot * hipsRot; + hipsPos = headPos + tiltRot * (hipsPos - headPos); + } + + return AnimPose(hipsRot * Quaternions::Y_180, hipsPos); +} + // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -124,6 +160,39 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } } + // if hips are not under direct control, estimate the hips position. + if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) { + bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS); + + if (!_prevHipsValid) { + AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); + _prevHips = hips; + } + + AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); + + // smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation. + const float ROT_ALPHA = 0.9f; + const float TRANS_HORIZ_ALPHA = 0.9f; + const float TRANS_VERT_ALPHA = 0.1f; + float hipsY = hips.trans().y; + hips.trans() = lerp(hips.trans(), _prevHips.trans(), TRANS_HORIZ_ALPHA); + hips.trans().y = lerp(hipsY, _prevHips.trans().y, TRANS_VERT_ALPHA); + hips.rot() = safeLerp(hips.rot(), _prevHips.rot(), ROT_ALPHA); + + _prevHips = hips; + _prevHipsValid = true; + + glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180); + AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix()); + + params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true; + + } else { + _prevHipsValid = false; + } + params.isTalking = head->getTimeWithoutTalking() <= 1.5f; // pass detailed torso k-dops to rig. diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index ad0ae1b8e9..d9f57a439a 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -25,6 +25,9 @@ public: private: void updateFingers(); + + AnimPose _prevHips; // sensor frame + bool _prevHipsValid { false }; }; #endif // hifi_MySkeletonModel_h From e709561a62bdc752025ff23b7cd15e2832b2bcdc Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 13 Dec 2017 12:14:49 -0700 Subject: [PATCH 122/137] Fix getActiveDevice deadlocks on Linux --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 942c9f71a5..12220a8079 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -679,7 +679,9 @@ void OffscreenQmlSurface::create() { // Setup the update of the QML media components with the current audio output device QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { - new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + if (_currentAudioOutputDevice.size() > 0) { + new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + } }); int waitForAudioQmlMs = 200; _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); @@ -695,6 +697,7 @@ void OffscreenQmlSurface::create() { } void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate) { + _currentAudioOutputDevice = deviceName; if (_rootItem != nullptr && !isHtmlUpdate) { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } @@ -702,18 +705,16 @@ void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, boo } void OffscreenQmlSurface::forceHtmlAudioOutputDeviceUpdate() { - auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, - Q_ARG(QString, deviceName), Q_ARG(bool, true)); + if (_currentAudioOutputDevice.size() > 0) { + QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, + Q_ARG(QString, _currentAudioOutputDevice), Q_ARG(bool, true)); + } } void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { if (QThread::currentThread() != qApp->thread()) { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } else { - auto audioIO = DependencyManager::get(); - _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); if (_audioOutputUpdateTimer.isActive()) { _audioOutputUpdateTimer.stop(); } From 627ce8ab87ffa252be83fca5f4851d0d0ecd550f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 14 Dec 2017 08:52:21 +1300 Subject: [PATCH 123/137] Enable user connection with "x" key when capslock is on --- scripts/system/makeUserConnection.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 392ff3c0b5..fedadbb2b4 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -892,12 +892,14 @@ } function keyPressEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { + if ((event.text.toUpperCase() === "X") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl + && !event.isAlt) { updateTriggers(1.0, true, Controller.Standard.RightHand); } } function keyReleaseEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { + if ((event.text.toUpperCase() === "X") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl + && !event.isAlt) { updateTriggers(0.0, true, Controller.Standard.RightHand); } } From efd5c0ccb7d1d4a40d88784bfa6c36b3b8e8edc5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 13 Dec 2017 11:53:26 -0800 Subject: [PATCH 124/137] Add comment as per CR --- interface/src/scripting/WalletScriptingInterface.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index a9948a68c6..5469e732c7 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -39,6 +39,8 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } + // setWalletStatus() should never be made Q_INVOKABLE. If it were, + // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); signals: From b4df1155076b67e7e3895d9056694ef539074833 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Wed, 13 Dec 2017 12:53:01 -0800 Subject: [PATCH 125/137] Protect against use of null HazePointer. --- libraries/render-utils/src/DrawHaze.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 4431c1bbc3..c92703fe87 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -169,7 +169,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu auto hazeStage = args->_scene->getStage(); if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); - batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); + if (hazePointer) { + batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); + } } batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer()); @@ -178,7 +180,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu if (lightStage) { model::LightPointer keyLight; keyLight = lightStage->getCurrentKeyLight(); - if (keyLight != nullptr) { + if (keyLight) { batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer()); } } From 79f012424477365ca9952dd573891e8091acd60c Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Wed, 13 Dec 2017 15:02:03 -0800 Subject: [PATCH 126/137] Protect against use of null HazePointer. --- libraries/render-utils/src/DrawHaze.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index c92703fe87..da07f5bd9b 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -171,6 +171,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); if (hazePointer) { batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); + } else { + // Something is wrong, so just quit Haze + return; } } From e581c66b5696ea27f73cdc5962623ea6542952b9 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 13 Dec 2017 16:11:46 -0700 Subject: [PATCH 127/137] added mutex for audio handler --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 4 +++- libraries/ui/src/ui/OffscreenQmlSurface.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 12220a8079..112d70c394 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -680,7 +680,9 @@ void OffscreenQmlSurface::create() { // Setup the update of the QML media components with the current audio output device QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { if (_currentAudioOutputDevice.size() > 0) { - new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + QMutexLocker lock(&_audioHandlerMutex); + QString audioDeviceName = _currentAudioOutputDevice; + new AudioHandler(sharedFromThis(), audioDeviceName); } }); int waitForAudioQmlMs = 200; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 4c23c62c12..370ddb4f2f 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -173,6 +173,7 @@ private: uint64_t _lastRenderTime { 0 }; uvec2 _size; + QMutex _audioHandlerMutex; QTimer _audioOutputUpdateTimer; QString _currentAudioOutputDevice; From 483443d2181f74f14b1c8b370afd047e995e6c04 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 13 Dec 2017 17:02:36 -0700 Subject: [PATCH 128/137] No mutex --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 4 +--- libraries/ui/src/ui/OffscreenQmlSurface.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 112d70c394..12220a8079 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -680,9 +680,7 @@ void OffscreenQmlSurface::create() { // Setup the update of the QML media components with the current audio output device QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { if (_currentAudioOutputDevice.size() > 0) { - QMutexLocker lock(&_audioHandlerMutex); - QString audioDeviceName = _currentAudioOutputDevice; - new AudioHandler(sharedFromThis(), audioDeviceName); + new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); } }); int waitForAudioQmlMs = 200; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 370ddb4f2f..4c23c62c12 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -173,7 +173,6 @@ private: uint64_t _lastRenderTime { 0 }; uvec2 _size; - QMutex _audioHandlerMutex; QTimer _audioOutputUpdateTimer; QString _currentAudioOutputDevice; From 0b598ea9483e5c79713a92ab43c249b14b797b0a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Dec 2017 17:26:23 -0800 Subject: [PATCH 129/137] fix RPATH for oven so it loads things from macdeployqt --- tools/oven/CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 321f81ba8f..5e30b84803 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -8,13 +8,14 @@ setup_memory_debugger() if (WIN32) package_libraries_for_deployment() -endif () - -if (UNIX) +elseif (UNIX) find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) target_compile_options(PUBLIC oven "-pthread") endif() -endif () +elseif (APPLE) + # Fix up the rpath so macdeployqt works + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif() install_beside_console() From 88831ff08a4923480cf18656aa7e7307d3fd82f2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Dec 2017 18:08:39 -0800 Subject: [PATCH 130/137] don't trigger unix step on apple for oven --- tools/oven/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 5e30b84803..00344179bd 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -8,7 +8,7 @@ setup_memory_debugger() if (WIN32) package_libraries_for_deployment() -elseif (UNIX) +elseif (UNIX AND NOT APPLE) find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) target_compile_options(PUBLIC oven "-pthread") From 5bc113c81844d9d7753c8214ab67dd6f41f60d7c Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 13 Dec 2017 19:07:23 -0800 Subject: [PATCH 131/137] deleted some debug print statements in ModelEntityItemcpp and deleted else statement that was superfluous --- libraries/entities/src/ModelEntityItem.cpp | 28 ++++------------------ 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 3215ab9dd0..323584c7ee 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -207,12 +207,10 @@ void ModelEntityItem::update(const quint64& now) { // don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set _currentFrame = currentAnimationProperties.getCurrentFrame(); setAnimationCurrentFrame(_currentFrame); - qCDebug(entities) << "setting first frame 1 " << _currentFrame; } else { _lastAnimated = usecTimestampNow(); _currentFrame = currentAnimationProperties.getFirstFrame(); setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); - qCDebug(entities) << "setting first frame 2" << _currentFrame; } } else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) { _currentFrame = currentAnimationProperties.getFirstFrame(); @@ -220,31 +218,11 @@ void ModelEntityItem::update(const quint64& now) { } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { // don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated _currentFrame = currentAnimationProperties.getCurrentFrame(); - // qCDebug(entities) << "point 3 " << _currentFrame; } }); _previousAnimationProperties = this->getAnimationProperties(); - } else { - // else the animation properties have not changed. - // if the first frame is less than zero don't do anything. - if (!(getAnimationFirstFrame() < 0)) { - - // if the current frame is less than zero then we have restarted the server. - if (_currentFrame < 0) { - //qCDebug(entities) << "setting first frame 3 " << _currentFrame; - if ((currentAnimationProperties.getCurrentFrame() < currentAnimationProperties.getLastFrame()) && - (currentAnimationProperties.getCurrentFrame() > currentAnimationProperties.getFirstFrame())) { - // _currentFrame = currentAnimationProperties.getCurrentFrame(); - } else { - //qCDebug(entities) << "setting first frame 4 " << _currentFrame; - // _currentFrame = currentAnimationProperties.getFirstFrame(); - // setAnimationCurrentFrame(_currentFrame); - // _lastAnimated = usecTimestampNow(); - } - } - } } if (isAnimatingSomething()) { @@ -263,6 +241,10 @@ bool ModelEntityItem::needsToCallUpdate() const { } void ModelEntityItem::updateFrameCount() { + + if (_currentFrame < 0.0f) { + return; + } if (!_lastAnimated) { _lastAnimated = usecTimestampNow(); @@ -298,7 +280,7 @@ void ModelEntityItem::updateFrameCount() { _currentFrame = getAnimationFirstFrame(); } } - qCDebug(entities) << "in update frame " << _currentFrame; + // qCDebug(entities) << "in update frame " << _currentFrame; setAnimationCurrentFrame(_currentFrame); } From b6a88bbdda6b380f0365e5c2a4a52a1c58690de4 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Dec 2017 10:20:46 -0800 Subject: [PATCH 132/137] Invalid WAV files should always be logged and not played --- libraries/audio/src/Sound.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 476a8d4d88..672c0b69b3 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -96,9 +96,13 @@ void SoundProcessor::run() { QByteArray outputAudioByteArray; int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); - if (sampleRate != 0) { - downSample(outputAudioByteArray, sampleRate); + if (sampleRate == 0) { + qCDebug(audio) << "Unsupported WAV file type"; + emit onError(300, "Failed to load sound file, reason: unsupported WAV file type"); + return; } + + downSample(outputAudioByteArray, sampleRate); } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw From 74a5f0881ff60159b583f269dcc7d7d74a8f9a12 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 14 Dec 2017 11:56:36 -0800 Subject: [PATCH 133/137] Fix broken Buy buttons on Marketplace --- scripts/system/html/js/marketplacesInject.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 878c3b51f1..b361f1120b 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -265,8 +265,10 @@ }); $('.grid-item').find('#price-or-edit').find('a').each(function() { - $(this).attr('data-href', $(this).attr('href')); - $(this).attr('href', '#'); + if ($(this).attr('href') !== '#') { // Guard necessary because of the AJAX nature of Marketplace site + $(this).attr('data-href', $(this).attr('href')); + $(this).attr('href', '#'); + } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); From 32acc8ad4780f746bbbbb26ca8233fe60230e0e4 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 8 Dec 2017 00:20:35 +0300 Subject: [PATCH 134/137] 8992 Login window is small in tablet --- interface/resources/qml/LoginDialog/LinkAccountBody.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 300bcd46f0..0e2f2a5282 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -52,7 +52,11 @@ Item { targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height } - parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); + var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); + if(!isNaN(newWidth)) { + parent.width = root.width = newWidth; + } + parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y); } From 53e1c57e3b2c4eee5fe08d3b6be3fce9487e148a Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 15 Dec 2017 13:46:15 -0700 Subject: [PATCH 135/137] Make lasers scale with avatar --- scripts/system/controllers/controllerDispatcher.js | 4 ++++ scripts/system/controllers/grab.js | 1 + 2 files changed, 5 insertions(+) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 915b3b3680..51f927f224 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -412,6 +412,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: LEFT_HAND }); @@ -421,6 +422,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: RIGHT_HAND }); @@ -431,6 +433,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: LEFT_HAND }); @@ -441,6 +444,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: RIGHT_HAND }); diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 7733ca1d28..a51cea67f8 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -269,6 +269,7 @@ function Grabber() { joint: "Mouse", filter: Picks.PICK_ENTITIES, faceAvatar: true, + scaleWithAvatar: true, enabled: true, renderStates: renderStates }); From e2df9e29e20a5944d85817b14180fb137833749f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Dec 2017 16:46:27 -0800 Subject: [PATCH 136/137] Fix for crash in AnimSkeleton::getNumJoints() When initAnimGraph is called it asynchronously loads the Animation graph in the background. If the model url is changed, or the Model is deleted in between the initial load and it's completion, It's possible to access a bad Rig::_animSkeleton pointer. The fix is to hold onto the _animSkeleton pointer via a weak ref. --- libraries/animation/src/Rig.cpp | 13 +++++++++++-- libraries/animation/src/Rig.h | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 78aa1f4ba8..44745c5c2d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1641,9 +1641,17 @@ void Rig::initAnimGraph(const QUrl& url) { // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); _animLoading = true; - connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { + std::weak_ptr weakSkeletonPtr = _animSkeleton; + connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { _animNode = nodeIn; - _animNode->setSkeleton(_animSkeleton); + + // abort load if the previous skeleton was deleted. + auto sharedSkeletonPtr = weakSkeletonPtr.lock(); + if (!sharedSkeletonPtr) { + return; + } + + _animNode->setSkeleton(sharedSkeletonPtr); if (_userAnimState.clipNodeEnum != UserAnimState::None) { // restore the user animation we had before reset. @@ -1651,6 +1659,7 @@ void Rig::initAnimGraph(const QUrl& url) { _userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); } + // restore the role animations we had before reset. for (auto& roleAnimState : _roleAnimStates) { auto roleState = roleAnimState.second; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e738ad1c19..1ec4d9527f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -31,7 +31,7 @@ class AnimInverseKinematics; // Rig instances are reentrant. // However only specific methods thread-safe. Noted below. -class Rig : public QObject, public std::enable_shared_from_this { +class Rig : public QObject { Q_OBJECT public: struct StateHandler { From 5e194102939e558c39ea6c7a63d4ffeb3e527a8d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 17 Dec 2017 15:23:40 +1300 Subject: [PATCH 137/137] Fix handshake satyhing on when release "x" if Ctrl or similar pressed --- scripts/system/makeUserConnection.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index fedadbb2b4..d9003ffeaa 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -898,8 +898,7 @@ } } function keyReleaseEvent(event) { - if ((event.text.toUpperCase() === "X") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl - && !event.isAlt) { + if (event.text.toUpperCase() === "X" && !event.isAutoRepeat) { updateTriggers(0.0, true, Controller.Standard.RightHand); } }