From 435c58c7cc32417ec7507340e9b79a43e60e8d7d Mon Sep 17 00:00:00 2001 From: Robin K Wilson Date: Wed, 20 Mar 2019 14:48:35 -0700 Subject: [PATCH 01/83] Update BUILD_WIN.md --- BUILD_WIN.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index a81fca5900..01ba2887a3 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -53,6 +53,10 @@ Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio. Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance. +Create another environment variable (see Step #4) +* Set "Variable name": `PreferredToolArchitecture` +* Set "Variable value": `x64` + Run from the menu bar `Build > Build Solution`. ### Step 7. Testing Interface From 90e979bda76db838eaabbb134f577897e86c5c36 Mon Sep 17 00:00:00 2001 From: Saracen Date: Sun, 31 Mar 2019 17:54:01 +0100 Subject: [PATCH 02/83] Add option to disable triggering an away state when interface focus is lost. --- interface/src/Application.cpp | 7 +++++++ interface/src/Application.h | 5 +++++ interface/src/scripting/HMDScriptingInterface.cpp | 11 +++++++++++ interface/src/scripting/HMDScriptingInterface.h | 11 +++++++++++ interface/src/ui/PreferencesDialog.cpp | 6 ++++++ scripts/system/away.js | 15 +++++++++++++-- 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 47f3b774b2..4be42256a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -962,6 +962,7 @@ const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; const QString DEFAULT_CURSOR_NAME = "DEFAULT"; const bool DEFAULT_MINI_TABLET_ENABLED = true; +const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true; QSharedPointer getOffscreenUI() { #if !defined(DISABLE_QML) @@ -992,6 +993,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), + _awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _miniTabletEnabledSetting("miniTabletEnabled", DEFAULT_MINI_TABLET_ENABLED), _scaleMirror(1.0f), @@ -3562,6 +3564,11 @@ void Application::setSettingConstrainToolbarPosition(bool setting) { getOffscreenUI()->setConstrainToolbarToCenterX(setting); } +void Application::setAwayStateWhenFocusLostInVREnabled(bool enabled) { + _awayStateWhenFocusLostInVREnabled.set(enabled); + emit awayStateWhenFocusLostInVRChanged(enabled); +} + void Application::setMiniTabletEnabled(bool enabled) { _miniTabletEnabledSetting.set(enabled); emit miniTabletEnabledChanged(enabled); diff --git a/interface/src/Application.h b/interface/src/Application.h index 762ac9585a..226b18b202 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -236,6 +236,9 @@ public: float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); } void setSettingConstrainToolbarPosition(bool setting); + float getAwayStateWhenFocusLostInVREnabled() { return _awayStateWhenFocusLostInVREnabled.get(); } + void setAwayStateWhenFocusLostInVREnabled(bool setting); + Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; } NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } @@ -357,6 +360,7 @@ signals: void loginDialogFocusDisabled(); void miniTabletEnabledChanged(bool enabled); + void awayStateWhenFocusLostInVRChanged(bool enabled); public slots: QVector pasteEntities(float x, float y, float z); @@ -660,6 +664,7 @@ private: Setting::Handle _preferStylusOverLaserSetting; Setting::Handle _preferAvatarFingerOverStylusSetting; Setting::Handle _constrainToolbarPosition; + Setting::Handle _awayStateWhenFocusLostInVREnabled; Setting::Handle _preferredCursor; Setting::Handle _miniTabletEnabledSetting; Setting::Handle _keepLogWindowOnTop { "keepLogWindowOnTop", false }; diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index a365b84a15..baca6250d2 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -30,6 +30,9 @@ HMDScriptingInterface::HMDScriptingInterface() { connect(qApp, &Application::miniTabletEnabledChanged, [this](bool enabled) { emit miniTabletEnabledChanged(enabled); }); + connect(qApp, &Application::awayStateWhenFocusLostInVRChanged, [this](bool enabled) { + emit awayStateWhenFocusLostInVRChanged(enabled); + }); } glm::vec3 HMDScriptingInterface::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const { @@ -137,6 +140,14 @@ bool HMDScriptingInterface::getMiniTabletEnabled() { return qApp->getMiniTabletEnabled(); } +void HMDScriptingInterface::setAwayStateWhenFocusLostInVREnabled(bool enabled) { + qApp->setAwayStateWhenFocusLostInVREnabled(enabled); +} + +bool HMDScriptingInterface::getAwayStateWhenFocusLostInVREnabled() { + return qApp->getAwayStateWhenFocusLostInVREnabled(); +} + QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { glm::vec3 hudIntersection; diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index d41e8951a2..2233ab46c2 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -375,6 +375,14 @@ signals: */ bool miniTabletEnabledChanged(bool enabled); + /**jsdoc + * Triggered when the altering the mode for going into an away state when the interface focus is lost in VR. + * @function HMD.awayStateWhenFocusLostInVRChanged + * @param {boolean} enabled - true if the setting to go into an away state in VR when the interface focus is lost is enabled, otherwise false. + * @returns {Signal} + */ + bool awayStateWhenFocusLostInVRChanged(bool enabled); + public: HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); @@ -411,6 +419,9 @@ public: void setMiniTabletEnabled(bool enabled); bool getMiniTabletEnabled(); + void setAwayStateWhenFocusLostInVREnabled(bool enabled); + bool getAwayStateWhenFocusLostInVREnabled(); + QVariant getPlayAreaRect(); QVector getSensorPositions(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index dbd24573ee..fe5849f375 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -89,6 +89,12 @@ void setupPreferences() { auto setter = [](bool value) { qApp->setSettingConstrainToolbarPosition(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Constrain Toolbar Position to Horizontal Center", getter, setter)); } + + { + auto getter = []()->bool { return qApp->getAwayStateWhenFocusLostInVREnabled(); }; + auto setter = [](bool value) { qApp->setAwayStateWhenFocusLostInVREnabled(value); }; + preferences->addPreference(new CheckPreference(UI_CATEGORY, "Go into away state when interface window loses focus in VR", getter, setter)); + } { auto getter = []()->float { return qApp->getDesktopTabletScale(); }; diff --git a/scripts/system/away.js b/scripts/system/away.js index 2af43b2055..2e4a5d507e 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -67,6 +67,8 @@ var avatarPosition = MyAvatar.position; var wasHmdMounted = HMD.mounted; var previousBubbleState = Users.getIgnoreRadiusEnabled(); +var enterAwayStateWhenFocusLostInVR = HMD.enterAwayStateWhenFocusLostInVR; + // some intervals we may create/delete var avatarMovedInterval; @@ -283,8 +285,10 @@ function maybeGoAway() { if (Reticle.mouseCaptured !== wasMouseCaptured) { wasMouseCaptured = !wasMouseCaptured; if (!wasMouseCaptured) { - goAway(); - return; + if (enterAwayStateWhenFocusLostInVR) { + goAway(); + return; + } } } @@ -349,9 +353,14 @@ eventMapping.from(Controller.Standard.Back).peek().to(goActive); eventMapping.from(Controller.Standard.Start).peek().to(goActive); Controller.enableMapping(eventMappingName); +function awayStateWhenFocusLostInVRChanged(enabled) { + enterAwayStateWhenFocusLostInVR = enabled; +} + Script.scriptEnding.connect(function () { Script.clearInterval(maybeIntervalTimer); goActive(); + HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged); Controller.disableMapping(eventMappingName); Controller.mousePressEvent.disconnect(goActive); Controller.keyPressEvent.disconnect(maybeGoActive); @@ -359,6 +368,8 @@ Script.scriptEnding.connect(function () { Messages.unsubscribe(CHANNEL_AWAY_ENABLE); }); +HMD.awayStateWhenFocusLostInVRChanged.connect(awayStateWhenFocusLostInVRChanged); + if (HMD.active && !HMD.mounted) { print("Starting script, while HMD is active and not mounted..."); goAway(true); From 73fd61dcf07ed22e9bf61405e076b88859a7e069 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 25 Apr 2019 13:14:04 +1200 Subject: [PATCH 03/83] Picks, PickType, RayPick JSDoc update --- interface/src/raypick/CollisionPick.cpp | 31 ++ interface/src/raypick/ParabolaPick.h | 17 ++ .../src/raypick/PickScriptingInterface.cpp | 167 +++++++---- .../src/raypick/PickScriptingInterface.h | 281 ++++++++++-------- .../src/raypick/PointerScriptingInterface.h | 2 +- interface/src/raypick/RayPick.h | 15 + .../src/raypick/RayPickScriptingInterface.h | 101 +++++-- interface/src/raypick/StylusPick.h | 12 + .../entities/src/EntityItemProperties.cpp | 2 +- libraries/pointers/src/Pick.h | 44 ++- libraries/render-utils/src/Model.cpp | 2 +- libraries/shared/src/PhysicsCollisionGroups.h | 4 +- libraries/shared/src/PickFilter.h | 28 ++ libraries/shared/src/RegisteredMetaTypes.h | 71 +++-- libraries/shared/src/ShapeInfo.cpp | 2 + 15 files changed, 512 insertions(+), 267 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 82d75257df..b3888ae3a0 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -57,6 +57,37 @@ void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::v } } +/**jsdoc + * An intersection result for a collision pick. + * + * @typedef {object} CollisionPickResult + * @property {boolean} intersects - truen if there is at least one intersection, false if there isn't. + * @property {IntersectingObject[]} intersectingObjects - All objects which intersect with the collisionRegion. + * @property {CollisionRegion} collisionRegion - The collision region that was used. Valid even if there was no intersection. + */ + +/**jsdoc + * Information about a {@link CollisionPick}'s intersection with an object. + * + * @typedef {object} IntersectingObject + * @property {Uuid} id - The ID of the object. + * @property {IntersectionType} type - The type of the object, either 1 for INTERSECTED_ENTITY or 3 + * for INTERSECTED_AVATAR. + * @property {CollisionContact[]} collisionContacts - Information on the penetration between the pick and the object. + */ + +/**jsdoc + * A pair of points that represents part of an overlap between a {@link CollisionPick} and an object in the physics engine. + * Points which are further apart represent deeper overlap. + * + * @typedef {object} CollisionContact + * @property {Vec3} pointOnPick - A point representing a penetration of the object's surface into the volume of the pick, in + * world coordinates. + * @property {Vec3} pointOnObject - A point representing a penetration of the pick's surface into the volume of the object, in + * world coordinates. + * @property {Vec3} normalOnPick - The normal vector pointing away from the pick, representing the direction of collision. + */ + QVariantMap CollisionPickResult::toVariantMap() const { QVariantMap variantMap; diff --git a/interface/src/raypick/ParabolaPick.h b/interface/src/raypick/ParabolaPick.h index 0adbb01954..78c35e13b4 100644 --- a/interface/src/raypick/ParabolaPick.h +++ b/interface/src/raypick/ParabolaPick.h @@ -42,6 +42,23 @@ public: float parabolicDistance { FLT_MAX }; bool intersects { false }; + /**jsdoc + * An intersection result for a parabola pick. + * + * @typedef {object} ParabolaPickResult + * @property {number} type - The intersection type. + * @property {boolean} intersects - true if there's a valid intersection, false if there isn't. + * @property {Uuid} objectID - The ID of the intersected object. null for HUD or invalid intersections. + * @property {number} distance - The distance from the parabola origin to the intersection point in a straight line. + * @property {number} parabolicDistance - The distance from the parabola origin to the intersection point along the arc of + * the parabola. + * @property {Vec3} intersection - The intersection point in world coordinates. + * @property {Vec3} surfaceNormal - The surface normal at the intersected point. All NaNs if type == + * Picks.INTERSECTED_HUD. + * @property {SubmeshIntersection} extraInfo - Additional intersection details for model objects, otherwise + * { }. + * @property {PickParabola} parabola - The pick parabola that was used. Valid even if there is no intersection. + */ virtual QVariantMap toVariantMap() const override { QVariantMap toReturn; toReturn["type"] = type; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 09f4b68cb9..7e87ac22a7 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -55,23 +55,37 @@ PickFilter getPickFilter(unsigned int filter) { } /**jsdoc - * A set of properties that can be passed to {@link Picks.createPick} to create a new Ray Pick. + * A set of properties that can be passed to {@link Picks.createPick} when creating a new ray pick. + * * @typedef {object} Picks.RayPickProperties - * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. - * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. - * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. - * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) - * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. - * @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral - * @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral - * @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray. - * @property {Vec3} [direction=-Vec3.UP] Only for Static Ray Picks. The world-space direction of the ray. + * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should + * start disabled. Disabled picks do not update their pick results. + * @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property + * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 + * means no maximum. + * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. + * A value of 0 means no joint.
+ * Used only if parentID is specified. + * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * parented to anything.
+ * Used only if parentID is not specified. + * @property {Vec3} [position=Vec3.ZERO] - The offset of the ray origin from its parent if parented, otherwise the ray origin + * in world coordinates. + * @property {Vec3} [posOffset] - Synonym for position. + * @property {Vec3} [direction] - The offset of the ray direction from its parent's y-axis if parented, otherwise the ray + * direction in world coordinates. + *

Default Value: Vec3.UP direction if joint is specified, otherwise + * -Vec3.UP.

+ * @property {Vec3} [dirOffset] - Synonym for direction. + * @property {Quat} [orientation] - Alternative property for specifying direction. The value is applied to the + * default direction value. */ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); - #if defined (Q_OS_ANDROID) QString jointName { "" }; if (propMap["joint"].isValid()) { @@ -124,12 +138,20 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { } /**jsdoc - * A set of properties that can be passed to {@link Picks.createPick} to create a new Stylus Pick. + * A set of properties that can be passed to {@link Picks.createPick} when creating a new stylus pick. + * * @typedef {object} Picks.StylusPickProperties - * @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise. - * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. - * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. + * @property {number} [hand=-1] 0 for the left hand, 1 for the right hand, invalid (-1) + * otherwise. + * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should + * start disabled. Disabled picks do not update their pick results. + * @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property + * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + *

Note: Stylus picks do not intersect avatars or the HUD.

+ * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 + * means no maximum. + * @property {Vec3} [tipOffset=0,0.095,0] - The position of the stylus tip relative to the hand position at default avatar + * scale. */ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); @@ -167,23 +189,45 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties // NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API. /**jsdoc - * A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick. + * A set of properties that can be passed to {@link Picks.createPick} when creating a new parabola pick. + * * @typedef {object} Picks.ParabolaPickProperties - * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. - * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. - * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. - * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) - * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. - * @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Parabola Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral - * @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Parabola Picks. A local joint direction offset. x = upward, y = forward, z = lateral - * @property {Vec3} [position] Only for Static Parabola Picks. The world-space origin of the parabola segment. - * @property {Vec3} [direction=-Vec3.FRONT] Only for Static Parabola Picks. The world-space direction of the parabola segment. - * @property {number} [speed=1] The initial speed of the parabola, i.e. the initial speed of the projectile whose trajectory defines the parabola. - * @property {Vec3} [accelerationAxis=-Vec3.UP] The acceleration of the parabola, i.e. the acceleration of the projectile whose trajectory defines the parabola, both magnitude and direction. - * @property {boolean} [rotateAccelerationWithAvatar=true] Whether or not the acceleration axis should rotate with the avatar's local Y axis. - * @property {boolean} [rotateAccelerationWithParent=false] Whether or not the acceleration axis should rotate with the parent's local Y axis, if available. - * @property {boolean} [scaleWithParent=true] If true, the velocity and acceleration of the Pick will scale linearly with the parent, if available. scaleWithAvatar is an alias but is deprecated. + * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should + * start disabled. Disabled picks do not update their pick results. + * @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property + * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 + * means no maximum. + * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. + * A value of 0 means no joint.
+ * Used only if parentID is specified. + * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * parented to anything. + * Used only if parentID is not specified. + * @property {Vec3} [position=Vec3.ZERO] - The offset of the parabola origin from its parent if parented, otherwise the + * parabola origin in world coordinates. + * @property {Vec3} [posOffset] - Synonym for position. + * @property {Vec3} [direction] - The offset of the parabola direction from its parent's y-axis if parented, otherwise the + * parabola direction in world coordinates. + *

Default Value: Vec3.UP direction if joint is specified, otherwise + * Vec3.FRONT.

+ * @property {Vec3} [dirOffset] - Synonym for direction. + * @property {Quat} [orientation] - Alternative property for specifying direction. The value is applied to the + * default direction value. + * @property {number} [speed=1] - The initial speed of the parabola in m/s, i.e., the initial speed of a virtual projectile + * whose trajectory defines the parabola. + * @property {Vec3} [accelerationAxis=-Vec3.UP] - The acceleration of the parabola in m/s2, i.e., the acceleration + * of a virtual projectile whose trajectory defines the parabola, both magnitude and direction. + * @property {boolean} [rotateAccelerationWithAvatar=true] - true if the acceleration axis should rotate with the + * avatar about the avatar's y-axis, false if it shouldn't. + * @property {boolean} [rotateAccelerationWithParent=false] - true if the acceleration axis should rotate with the + * parent about the parent's y-axis, if available. + * @property {boolean} [scaleWithParent=true] - true if the velocity and acceleration of the pick should scale + * with the avatar or other parent. + * @property {boolean} [scaleWithAvatar=true] - Synonym for scalewithParent. + *

Deprecated: This property is deprecated and will be removed.

*/ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); @@ -251,35 +295,38 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti return DependencyManager::get()->addPick(PickQuery::Parabola, parabolaPick); } -/**jsdoc -* A Shape defines a physical volume. -* -* @typedef {object} Shape -* @property {string} shapeType The type of shape to use. Can be one of the following: "box", "sphere", "capsule-x", "capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z" -* @property {Vec3} dimensions - The size to scale the shape to. -*/ - -// TODO: Add this property to the Shape jsdoc above once model picks work properly -// * @property {string} modelURL - If shapeType is one of: "compound", "simple-hull", "simple-compound", or "static-mesh", this defines the model to load to generate the collision volume. /**jsdoc -* A set of properties that can be passed to {@link Picks.createPick} to create a new Collision Pick. - -* @typedef {object} Picks.CollisionPickProperties -* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. -* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. -* @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined. -* @property {Vec3} position - The position of the collision region, relative to a parent if defined. -* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. -* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. -* The depth is measured in world space, but will scale with the parent if defined. -* @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group -* will be considered colliding with the pick. -* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or a pick. -* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) -* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. -* @property {boolean} [scaleWithParent=true] If true, the collision pick's dimensions and threshold will adjust according to the scale of the parent. -*/ + * A set of properties that can be passed to {@link Picks.createPick} when creating a new collision pick. + * + * @typedef {object} Picks.CollisionPickProperties + * @property {boolean} [enabled=false] - true if this pick should start enabled, false if it should + * start disabled. Disabled picks do not update their pick results. + * @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property + * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. + *

Note: Collision picks do not intersect the HUD.

+ * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 + * means no maximum. + * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. + * A value of 0 means no joint.
+ * Used only if parentID is specified. + * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * parented to anything.
+ * Used only if parentID is not specified. + * @property {boolean} [scaleWithParent=true] - true to scale the pick's dimensions and threshold according to the + * scale of the parent. + * + * @property {Shape} shape - The collision region's shape and size. Dimensions are in world coordinates but scale with the + * parent if defined. + * @property {Vec3} position - The position of the collision region, relative to the parent if defined. + * @property {Quat} orientation - The orientation of the collision region, relative to the parent if defined. + * @property {number} threshold - The approximate minimum penetration depth for a test object to be considered in contact with + * the collision region. The depth is in world coordinates but scales with the parent if defined. + * @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision + * masks overlap with the regions's collision group are considered to be colliding with the region. + */ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 6264a3e258..e544262b5f 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -17,7 +17,7 @@ #include /**jsdoc - * The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways. + * The Picks API lets you create and manage objects for repeatedly calculating intersections. * * @namespace Picks * @@ -25,33 +25,43 @@ * @hifi-client-entity * @hifi-avatar * - * @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. Read-only.. Deprecated. - * @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. Read-only.. Deprecated. + * @property {FilterFlags} PICK_DOMAIN_ENTITIES - Include domain entities when intersecting. Read-only. + * @property {FilterFlags} PICK_AVATAR_ENTITIES - Include avatar entities when intersecting. Read-only. + * @property {FilterFlags} PICK_LOCAL_ENTITIES - Include local entities when intersecting. Read-only. + * @property {FilterFlags} PICK_AVATARS - Include avatars when intersecting. Read-only. + * @property {FilterFlags} PICK_HUD - Include the HUD sphere when intersecting in HMD mode. Read-only. * - * @property {number} PICK_DOMAIN_ENTITIES A filter flag. Include domain entities when intersecting. Read-only.. - * @property {number} PICK_AVATAR_ENTITIES A filter flag. Include avatar entities when intersecting. Read-only.. - * @property {number} PICK_LOCAL_ENTITIES A filter flag. Include local entities when intersecting. Read-only.. - * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only.. - * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only.. + * @property {FilterFlags} PICK_ENTITIES - Include domain and avatar entities when intersecting. Read-only. + *

Deprecated: Use PICK_DOMAIN_ENTITIES | PICK_AVATAR_ENTITIES + * instead.

+ * @property {FilterFlags} PICK_OVERLAYS - Include local entities when intersecting. Read-only. + *

Deprecated: Use PICK_LOCAL_ENTITIES instead.

* - * @property {number} PICK_INCLUDE_VISIBLE A filter flag. Include visible objects when intersecting. Read-only.. - * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only.. + * @property {FilterFlags} PICK_INCLUDE_VISIBLE - Include visible objects when intersecting. Read-only. + *

Warning: Is currently always enabled by default but may not be in the future.

+ * @property {FilterFlags} PICK_INCLUDE_INVISIBLE - Include invisible objects when intersecting. Read-only. * - * @property {number} PICK_INCLUDE_COLLIDABLE A filter flag. Include collidable objects when intersecting. Read-only.. - * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. Read-only.. + * @property {FilterFlags} PICK_INCLUDE_COLLIDABLE - Include collidable objects when intersecting. Read-only. + *

Warning: Is currently always enabled by default but may not be in the future.

+ * @property {FilterFlags} PICK_INCLUDE_NONCOLLIDABLE - Include non-collidable objects when intersecting. Read-only. * - * @property {number} PICK_PRECISE A filter flag. Pick against exact meshes. Read-only.. - * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes. Read-only.. + * @property {FilterFlags} PICK_PRECISE - Pick against exact meshes. Read-only. + * @property {FilterFlags} PICK_COARSE - Pick against coarse meshes. Read-only. * - * @property {number} PICK_ALL_INTERSECTIONS Read-only.. + * @property {FilterFlags} PICK_ALL_INTERSECTIONS - If set, returns all intersections instead of just the closest. + * Read-only. + *

Warning: Not yet implemented.

* - * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. Read-only. - * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. Read-only. - * @property {number} INTERSECTED_LOCAL_ENTITY An intersection type. Intersected a local entity. - * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an entity (3D Overlays no longer exist). Read-only. - * @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. Read-only. - * @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. Read-only. - * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. + * @property {IntersectionType} INTERSECTED_NONE - Intersected nothing. Read-only. + * @property {IntersectionType} INTERSECTED_ENTITY - Intersected an entity. Read-only. + * @property {IntersectionType} INTERSECTED_LOCAL_ENTITY - Intersected a local entity. Read-only. + * @property {IntersectionType} INTERSECTED_OVERLAY - Intersected a local entity. (3D overlays no longer exist.) + * Read-only. + *

Deprecated: Use INTERSECTED_LOCAL_ENTITY instead.

+ * @property {IntersectionType} INTERSECTED_AVATAR - Intersected an avatar. Read-only. + * @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD sphere. Read-only. + * + * @property {number} perFrameTimeBudget - The maximum time, in microseconds, to spend per frame updating pick results. */ class PickScriptingInterface : public QObject, public Dependency { @@ -94,163 +104,145 @@ public: void registerMetaTypes(QScriptEngine* engine); /**jsdoc - * Adds a new Pick. - * Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, - * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick. - * Picks created with this method always intersect at least visible and collidable things + * Creates a new pick. Different {@link PickType}s use different properties, and within one PickType the properties you + * choose can lead to a wide range of behaviors. For example, with PickType.Ray, the properties could + * configure a mouse ray pick, an avatar head ray pick, or a joint ray pick. + *

Warning: Picks created with this method currently always intersect at least visible and collidable + * things but this may not always be the case.

* @function Picks.createPick - * @param {PickType} type A PickType that specifies the method of picking to use - * @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties|Picks.CollisionPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick - * @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid. + * @param {PickType} type - The type of picking to use. + * @param {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties} + * properties - Properties of the pick, per the pick type. + * @returns {number} The ID of the pick created. 0 if invalid. */ // TODO: expand Pointers to be able to be fully configurable with PickFilters Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); /**jsdoc - * Enables a Pick. + * Enables a pick. enabled picks update their pick results. * @function Picks.enablePick - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {number} id - The ID of the pick. */ Q_INVOKABLE void enablePick(unsigned int uid); /**jsdoc - * Disables a Pick. + * Disables a pick. Disabled picks do not update their pick results. * @function Picks.disablePick - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {number} id - The ID of the pick. */ Q_INVOKABLE void disablePick(unsigned int uid); /**jsdoc - * Removes a Pick. + * Removes (deletes) a pick. * @function Picks.removePick - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {number} id - The ID of the pick. */ Q_INVOKABLE void removePick(unsigned int uid); /**jsdoc - * An intersection result for a Ray Pick. - * - * @typedef {object} RayPickResult - * @property {number} type The intersection type. - * @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) - * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. - * @property {number} distance The distance to the intersection point from the origin of the ray. - * @property {Vec3} intersection The intersection point in world-space. - * @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD. - * @property {Variant} extraInfo Additional intersection details when available for Model objects. - * @property {PickRay} searchRay The PickRay that was used. Valid even if there was no intersection. - */ - - /**jsdoc - * An intersection result for a Stylus Pick. - * - * @typedef {object} StylusPickResult - * @property {number} type The intersection type. - * @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) - * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. - * @property {number} distance The distance to the intersection point from the origin of the ray. - * @property {Vec3} intersection The intersection point in world-space. - * @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD. - * @property {Variant} extraInfo Additional intersection details when available for Model objects. - * @property {StylusTip} stylusTip The StylusTip that was used. Valid even if there was no intersection. - */ - - /**jsdoc - * An intersection result for a Parabola Pick. - * - * @typedef {object} ParabolaPickResult - * @property {number} type The intersection type. - * @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) - * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. - * @property {number} distance The distance to the intersection point from the origin of the parabola, not along the parabola. - * @property {number} parabolicDistance The distance to the intersection point from the origin of the parabola, along the parabola. - * @property {Vec3} intersection The intersection point in world-space. - * @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD. - * @property {Variant} extraInfo Additional intersection details when available for Model objects. - * @property {PickParabola} parabola The PickParabola that was used. Valid even if there was no intersection. - */ - - /**jsdoc - * An intersection result for a Collision Pick. - * - * @typedef {object} CollisionPickResult - * @property {boolean} intersects If there was at least one valid intersection (intersectingObjects.length > 0) - * @property {IntersectingObject[]} intersectingObjects The collision information of each object which intersect with the CollisionRegion. - * @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection. - */ - - /**jsdoc - * Information about the Collision Pick's intersection with an object - * - * @typedef {object} IntersectingObject - * @property {QUuid} id The ID of the object. - * @property {number} type The type of the object, either Picks.INTERSECTED_ENTITY() or Picks.INTERSECTED_AVATAR() - * @property {CollisionContact[]} collisionContacts Pairs of points representing penetration information between the pick and the object - */ - - /**jsdoc - * A pair of points that represents part of an overlap between a Collision Pick and an object in the physics engine. Points which are further apart represent deeper overlap - * - * @typedef {object} CollisionContact - * @property {Vec3} pointOnPick A point representing a penetration of the object's surface into the volume of the pick, in world space. - * @property {Vec3} pointOnObject A point representing a penetration of the pick's surface into the volume of the found object, in world space. - * @property {Vec3} normalOnPick The normalized vector pointing away from the pick, representing the direction of collision. - */ - - /**jsdoc - * Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled. + * Gets the most recent result from a pick. A pick continues to be updated ready to return a result, as long as it is + * enabled. + *

Note: Stylus picks only intersect with objects in their include list, set using + * {@link Picks.setIncludeItems|setIncludeItems}.

* @function Picks.getPrevPickResult - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @returns {RayPickResult|StylusPickResult|ParabolaPickResult|CollisionPickResult} The most recent intersection result. This will be different for different PickTypes. + * @param {number} id - The ID of the pick. + * @returns {RayPickResult|ParabolaPickResult|StylusPickResult|CollisionPickResult} The most recent intersection result. + * @example Highlight entities under your mouse in desktop mode or that you're looking at in HMD mode. + * // Highlight. + * var HIGHLIGHT_LIST_NAME = "highlightEntitiesExampleList"; + * var HIGHLIGHT_LIST_TYPE = "entity"; + * Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, {}); + * + * // Ray pick. + * var PICK_FILTER = Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_AVATAR_ENTITIES + * | Picks.PICK_INCLUDE_COLLIDABLE | Picks.PICK_INCLUDE_NONCOLLIDABLE; + * var rayPick = Picks.createPick(PickType.Ray, { + * enabled: true, + * filter: PICK_FILTER, + * joint: HMD.active ? "Avatar" : "Mouse" + * }); + * + * // Highlight intersected entity. + * var highlightedEntityID = null; + * Script.update.connect(function () { + * var rayPickResult = Picks.getPrevPickResult(rayPick); + * if (rayPickResult.intersects) { + * if (rayPickResult.objectID !== highlightedEntityID) { + * if (highlightedEntityID) { + * Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID); + * } + * highlightedEntityID = rayPickResult.objectID; + * Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID); + * } + * } else { + * if (highlightedEntityID) { + * Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID); + * highlightedEntityID = null; + * } + * } + * }); + * + * // Clean up. + * Script.scriptEnding.connect(function () { + * if (highlightedEntityID) { + * Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID); + * } + * }); */ Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid); /**jsdoc - * Sets whether or not to use precision picking. + * Sets whether or not to use precision picking, i.e., whether to pick against precise meshes or coarse meshes. + * This has the same effect as using the PICK_PRECISE or PICK_COARSE filter flags. * @function Picks.setPrecisionPicking - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @param {boolean} precisionPicking Whether or not to use precision picking + * @param {number} id - The ID of the pick. + * @param {boolean} precisionPicking - true to use precision picking, false to use coarse picking. */ Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); /**jsdoc - * Sets a list of Entity IDs and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks. + * Sets a list of entity and avatar IDs to ignore during intersection. + *

Note: Not used by stylus picks.

* @function Picks.setIgnoreItems - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @param {Uuid[]} ignoreItems A list of IDs to ignore. + * @param {number} id - The ID of the pick. + * @param {Uuid[]} ignoreItems - The list of IDs to ignore. */ Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems); /**jsdoc - * Sets a list of Entity IDs and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus - * Picks only intersect with objects in their include list. + * Sets a list of entity IDs and/or avatar IDs to include during intersection, instead of intersecting with everything. + *

Note: Stylus picks only intersect with objects in their include list.

* @function Picks.setIncludeItems - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @param {Uuid[]} includeItems A list of IDs to include. + * @param {number} id - The ID of the pick. + * @param {Uuid[]} includeItems - The list of IDs to include. */ Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeItems); /**jsdoc - * Check if a Pick is associated with the left hand. + * Checks if a pick is associated with the left hand: a ray or parabola pick with joint set to + * "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a stylus pick with hand + * set to 0. * @function Picks.isLeftHand - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @returns {boolean} True if the Pick is a Joint Ray or Parabola Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0. + * @param {number} id - The ID of the pick. + * @returns {boolean} true if the pick is associated with the left hand, false if it isn't. */ Q_INVOKABLE bool isLeftHand(unsigned int uid); /**jsdoc - * Check if a Pick is associated with the right hand. + * Checks if a pick is associated with the right hand: a ray or parabola pick with joint set to + * "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a stylus pick with hand + * set to 1. * @function Picks.isRightHand - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @returns {boolean} True if the Pick is a Joint Ray or Parabola Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1. + * @param {number} id - The ID of the pick. + * @returns {boolean} true if the pick is associated with the right hand, false if it isn't. */ Q_INVOKABLE bool isRightHand(unsigned int uid); /**jsdoc - * Check if a Pick is associated with the system mouse. + * Checks if a pick is associated with the system mouse: a ray or parabola pick with joint set to "Mouse". * @function Picks.isMouse - * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. - * @returns {boolean} True if the Pick is a Mouse Ray or Parabola Pick, false otherwise. + * @param {number} id - The ID of the pick. + * @returns {boolean} true if the pick is associated with the system mouse, false if it isn't. */ Q_INVOKABLE bool isMouse(unsigned int uid); @@ -261,112 +253,145 @@ public slots: /**jsdoc * @function Picks.PICK_ENTITIES + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } + /**jsdoc * @function Picks.PICK_OVERLAYS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } + /**jsdoc * @function Picks.PICK_DOMAIN_ENTITIES + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_DOMAIN_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); } + /**jsdoc * @function Picks.PICK_AVATAR_ENTITIES + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_AVATAR_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } + /**jsdoc * @function Picks.PICK_LOCAL_ENTITIES + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_LOCAL_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } + /**jsdoc * @function Picks.PICK_AVATARS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); } + /**jsdoc * @function Picks.PICK_HUD + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); } + /**jsdoc * @function Picks.PICK_INCLUDE_VISIBLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_VISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); } + /**jsdoc * @function Picks.PICK_INCLUDE_INVISIBLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); } + /**jsdoc * @function Picks.PICK_INCLUDE_COLLIDABLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_COLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); } + /**jsdoc * @function Picks.PICK_INCLUDE_NONCOLLIDABLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); } + /**jsdoc * @function Picks.PICK_PRECISE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_PRECISE() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); } + /**jsdoc * @function Picks.PICK_COARSE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } + /**jsdoc * @function Picks.PICK_ALL_INTERSECTIONS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int PICK_ALL_INTERSECTIONS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ALL_INTERSECTIONS); } /**jsdoc * @function Picks.INTERSECTED_NONE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_NONE() { return IntersectionType::NONE; } /**jsdoc * @function Picks.INTERSECTED_ENTITY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_ENTITY() { return IntersectionType::ENTITY; } /**jsdoc - * @function Picks.INTERSECTED_OVERLAY + * @function Picks.INTERSECTED_LOCAL_ENTITY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_LOCAL_ENTITY() { return IntersectionType::LOCAL_ENTITY; } /**jsdoc * @function Picks.INTERSECTED_OVERLAY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_OVERLAY() { return INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function Picks.INTERSECTED_AVATAR + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_AVATAR() { return IntersectionType::AVATAR; } /**jsdoc * @function Picks.INTERSECTED_HUD + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; } diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index b04d15d888..268b178fb6 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -49,7 +49,7 @@ public: * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer. * Pointers created with this method always intersect at least visible and collidable things * @function Pointers.createPointer - * @param {PickType} type A PickType that specifies the method of picking to use + * @param {PickType} type A PickType that specifies the method of picking to use. Cannot be {@link PickType|PickType.Collision}. * @param {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer and the {@link Picks.PickProperties} for the Pick that * this Pointer will use to do its picking. * @returns {number} The ID of the created Pointer. Used for managing the Pointer. 0 if invalid. diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 161c930fef..170a0489da 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -39,6 +39,21 @@ public: float distance { FLT_MAX }; bool intersects { false }; + /**jsdoc + * An intersection result for a ray pick. + * + * @typedef {object} RayPickResult + * @property {IntersectionType} type - The intersection type. + * @property {boolean} intersects - true if there's a valid intersection, false if there isn't. + * @property {Uuid} objectID - The ID of the intersected object. null for HUD or invalid intersections. + * @property {number} distance - The distance from the ray origin to the intersection point. + * @property {Vec3} intersection - The intersection point in world coordinates. + * @property {Vec3} surfaceNormal - The surface normal at the intersected point. All NaNs if type == + * Picks.INTERSECTED_HUD. + * @property {SubmeshIntersection} extraInfo - Additional intersection details for model objects, otherwise + * { }. + * @property {PickRay} searchRay - The pick ray that was used. Valid even if there is no intersection. + */ virtual QVariantMap toVariantMap() const override { QVariantMap toReturn; toReturn["type"] = type; diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 64b21ea055..32b2b8fb68 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -19,29 +19,32 @@ #include "PickScriptingInterface.h" /**jsdoc - * Synonym for {@link Picks} as used for ray picks. Deprecated. + * The RayPick API is a subset of the {@link Picks} API, as used for ray picks. * * @namespace RayPick * + * @deprecated This API is deprecated and will be removed. Use the {@link Picks} API instead. + * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {number} PICK_ENTITIES Read-only. - * @property {number} PICK_OVERLAYS Read-only. - * @property {number} PICK_AVATARS Read-only. - * @property {number} PICK_HUD Read-only. - * @property {number} PICK_COARSE Read-only. - * @property {number} PICK_INCLUDE_INVISIBLE Read-only. - * @property {number} PICK_INCLUDE_NONCOLLIDABLE Read-only. - * @property {number} PICK_ALL_INTERSECTIONS Read-only. - * @property {number} INTERSECTED_NONE Read-only. - * @property {number} INTERSECTED_ENTITY Read-only. - * @property {number} INTERSECTED_OVERLAY Read-only. - * @property {number} INTERSECTED_AVATAR Read-only. - * @property {number} INTERSECTED_HUD Read-only. + * @property {FilterFlags} PICK_ENTITIES - Include domain and avatar entities when intersecting. + * Read-only. + * @property {FilterFlags} PICK_OVERLAYS - Include local entities when intersecting. Read-only. + * @property {FilterFlags} PICK_AVATARS - Include avatars when intersecting. Read-only. + * @property {FilterFlags} PICK_HUD - Include the HUD sphere when intersecting in HMD mode. Read-only. + * @property {FilterFlags} PICK_PRECISE - Pick against exact meshes. Read-only. + * @property {FilterFlags} PICK_INCLUDE_INVISIBLE - Include invisible objects when intersecting. Read-only. + * @property {FilterFlags} PICK_INCLUDE_NONCOLLIDABLE - Include non-collidable objects when intersecting. Read-only. + * @property {FilterFlags} PICK_ALL_INTERSECTIONS - Return all intersections instead of just the closest. Read-only. + * @property {IntersectionType} INTERSECTED_NONE - Intersected nothing with the given filter flags. Read-only. + * @property {IntersectionType} INTERSECTED_ENTITY - Intersected an entity. Read-only. + * @property {IntersectionType} INTERSECTED_LOCAL_ENTITY - Intersected a local entity. Read-only. + * @property {IntersectionType} INTERSECTED_OVERLAY - Intersected an entity (3D Overlays no longer exist). Read-only. + * @property {IntersectionType} INTERSECTED_AVATAR - Intersected an avatar. Read-only. + * @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD sphere. Read-only. */ - class RayPickScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT) @@ -63,78 +66,96 @@ class RayPickScriptingInterface : public QObject, public Dependency { public: /**jsdoc + * Creates a new ray pick. + *

Warning: Picks created with this method currently always intersect at least visible and collidable + * things but this may not always be the case.

* @function RayPick.createRayPick - * @param {Picks.RayPickProperties} - * @returns {number} + * @param {Picks.RayPickProperties} properties - Properties of the pick. + * @returns {number} The ID of the pick created. 0 if invalid. */ Q_INVOKABLE unsigned int createRayPick(const QVariant& properties); /**jsdoc + * Enables a ray pick. * @function RayPick.enableRayPick - * @param {number} id + * @param {number} id - The ID of the ray pick. */ Q_INVOKABLE void enableRayPick(unsigned int uid); /**jsdoc + * Disables a ray pick. * @function RayPick.disableRayPick - * @param {number} id + * @param {number} id - The ID of the ray pick. */ Q_INVOKABLE void disableRayPick(unsigned int uid); /**jsdoc + * Removes (deletes) a ray pick. * @function RayPick.removeRayPick - * @param {number} id + * @param {number} id - The ID of the ray pick. */ Q_INVOKABLE void removeRayPick(unsigned int uid); /**jsdoc + * Gets the most recent pick result from a ray pick. A ray pick continues to be updated ready to return a result, as long + * as it is enabled. * @function RayPick.getPrevRayPickResult - * @param {number} id + * @param {number} id - The ID of the ray pick. * @returns {RayPickResult} */ Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid); /**jsdoc + * Sets whether or not to use precision picking, i.e., whether to pick against precise meshes or coarse meshes. * @function RayPick.setPrecisionPicking - * @param {number} id - * @param {boolean} precisionPicking + * @param {number} id - The ID of the ray pick. + * @param {boolean} precisionPicking - true to use precision picking, false to use coarse picking. */ Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); /**jsdoc + * Sets a list of entity and avatar IDs to ignore during intersection. * @function RayPick.setIgnoreItems - * @param {number} id - * @param {Uuid[]) ignoreEntities + * @param {number} id - The ID of the ray pick. + * @param {Uuid[]} ignoreItems - The list of IDs to ignore. */ Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities); /**jsdoc + * Sets a list of entity IDs and/or avatar IDs to include during intersection, instead of intersecting with everything. * @function RayPick.setIncludeItems - * @param {number} id - * @param {Uuid[]) includeEntities + * @param {number} id - The ID of the ray pick. + * @param {Uuid[]} includeItems - The list of IDs to include. */ Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities); /**jsdoc + * Checks if a pick is associated with the left hand: a ray or parabola pick with joint set to + * "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a stylus pick with hand + * set to 0. * @function RayPick.isLeftHand - * @param {number} id - * @returns {boolean} + * @param {number} id - The ID of the ray pick. + * @returns {boolean} true if the pick is associated with the left hand, false if it isn't. */ Q_INVOKABLE bool isLeftHand(unsigned int uid); /**jsdoc + * Checks if a pick is associated with the right hand: a ray or parabola pick with joint set to + * "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a stylus pick with hand + * set to 1. * @function RayPick.isRightHand - * @param {number} id - * @returns {boolean} + * @param {number} id - The ID of the ray pick. + * @returns {boolean} true if the pick is associated with the right hand, false if it isn't. */ Q_INVOKABLE bool isRightHand(unsigned int uid); /**jsdoc + * Checks if a pick is associated with the system mouse: a ray or parabola pick with joint set to "Mouse". * @function RayPick.isMouse - * @param {number} id - * @returns {boolean} + * @param {number} id - The ID of the ray pick. + * @returns {boolean} true if the pick is associated with the system mouse, false if it isn't. */ Q_INVOKABLE bool isMouse(unsigned int uid); @@ -142,84 +163,98 @@ public slots: /**jsdoc * @function RayPick.PICK_ENTITIES + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); } /**jsdoc * @function RayPick.PICK_OVERLAYS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); } /**jsdoc * @function RayPick.PICK_AVATARS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_AVATARS() { return PickScriptingInterface::PICK_AVATARS(); } /**jsdoc * @function RayPick.PICK_HUD + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_HUD() { return PickScriptingInterface::PICK_HUD(); } /**jsdoc * @function RayPick.PICK_COARSE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_COARSE() { return PickScriptingInterface::PICK_COARSE(); } /**jsdoc * @function RayPick.PICK_INCLUDE_INVISIBLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_INCLUDE_INVISIBLE() { return PickScriptingInterface::PICK_INCLUDE_INVISIBLE(); } /**jsdoc * @function RayPick.PICK_INCLUDE_NONCOLLIDABLE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE(); } /**jsdoc * @function RayPick.PICK_ALL_INTERSECTIONS + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int PICK_ALL_INTERSECTIONS() { return PickScriptingInterface::PICK_ALL_INTERSECTIONS(); } /**jsdoc * @function RayPick.INTERSECTED_NONE + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_NONE() { return PickScriptingInterface::INTERSECTED_NONE(); } /**jsdoc * @function RayPick.INTERSECTED_ENTITY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_ENTITY() { return PickScriptingInterface::INTERSECTED_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_OVERLAY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_LOCAL_ENTITY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_OVERLAY + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_AVATAR + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_AVATAR() { return PickScriptingInterface::INTERSECTED_AVATAR(); } /**jsdoc * @function RayPick.INTERSECTED_HUD + * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. * @returns {number} */ static unsigned int INTERSECTED_HUD() { return PickScriptingInterface::INTERSECTED_HUD(); } diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index 9d5dc10b67..e443fb1c6d 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -38,6 +38,18 @@ public: glm::vec3 intersection { NAN }; glm::vec3 surfaceNormal { NAN }; + /**jsdoc + * An intersection result for a stylus pick. + * + * @typedef {object} StylusPickResult + * @property {number} type - The intersection type. + * @property {boolean} intersects - true if there's a valid intersection, false if there isn't. + * @property {Uuid} objectID - The ID of the intersected object. null for invalid intersections. + * @property {number} distance - The distance to the intersection point from the stylus tip. + * @property {Vec3} intersection - The intersection point in world coordinates. + * @property {Vec3} surfaceNormal - The surface normal at the intersected point. + * @property {StylusTip} stylusTip - The stylus tip at the time of the result. Valid even if there is no intersection. + */ virtual QVariantMap toVariantMap() const override { QVariantMap toReturn; toReturn["type"] = type; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 2b738bc4e7..5d39139b46 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -801,7 +801,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {boolean} collisionless=false - Whether or not the entity should collide with items per its * collisionMask property. If true then the entity does not collide. A synonym is ignoreForCollisions. - * @property {Entities.CollisionMask} collisionMask=31 - What types of items the entity should collide with. + * @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with. * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, * in text format. * @property {string} collisionSoundURL="" - The sound to play when the entity experiences a collision. Valid file formats are diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index dda35a53a2..1766736664 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -20,6 +20,22 @@ #include #include +/**jsdoc + *

The type of an intersection.

+ * + * + * + * + * + * + * + * + * + * + * + *
NameValueDescription
INTERSECTED_NONE0Intersected nothing.
INTERSECTED_ENTITY1Intersected an entity.
INTERSECTED_LOCAL_ENTITY2Intersected a local entity.
INTERSECTED_AVATAR3Intersected an avatar.
INTERSECTED_HUD4Intersected the HUD sphere.
+ * @typedef {number} IntersectionType + */ enum IntersectionType { NONE = 0, ENTITY, @@ -61,7 +77,7 @@ public: virtual ~PickQuery() = default; /**jsdoc - * Enum for different types of Picks and Pointers. + * The PickType API provides constant numeric values that represent different types of picks. * * @namespace PickType * @variation 0 @@ -70,22 +86,28 @@ public: * @hifi-client-entity * @hifi-avatar * - * @property {number} Ray Ray picks intersect a ray with the nearest object in front of them, along a given direction. - * @property {number} Stylus Stylus picks provide "tapping" functionality on/into flat surfaces. - * @property {number} Parabola Parabola picks intersect a parabola with the nearest object in front of them, with a given - * initial velocity and acceleration. - * @property {number} Collision Collision picks intersect a collision volume with avatars and entities that have collisions. + * @property {number} Ray - Ray picks intersect a ray with objects in front of them, along their direction. + * @property {number} Parabola - Parabola picks intersect a parabola with objects in front of them, along their arc. + * @property {number} Stylus - Stylus picks provide "tapping" functionality on or into flat surfaces. + * @property {number} Collision - Collision picks intersect a collision volume with avatars and entities that have + * collisions. */ - /**jsdoc + + /**jsdoc + *

A type of pick.

* * * * * - * - * - * - * + * + * + * + * * *
ValueDescription
{@link PickType(0)|PickType.Ray}
{@link PickType(0)|PickType.Stylus}
{@link PickType(0)|PickType.Parabola}
{@link PickType(0)|PickType.Collision}
{@link PickType(0)|PickType.Ray}Ray picks intersect a ray with objects in front of + * them, along their direction.
{@link PickType(0)|PickType.Parabola}Parabola picks intersect a parabola with objects + * in front of them, along their arc.
{@link PickType(0)|PickType.Stylus}Stylus picks provide "tapping" functionality on or + * into flat surfaces.
{@link PickType(0)|PickType.Collision}Collision picks intersect a collision volume + * with avatars and entities that have collisions.
* @typedef {number} PickType diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e2d78a8d94..9d24b8e706 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -445,7 +445,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } /**jsdoc - * Information about a submesh intersection point. + * A submesh intersection point. * @typedef {object} SubmeshIntersection * @property {Vec3} worldIntersectionPoint - The intersection point in world coordinates. * @property {Vec3} meshIntersectionPoint - The intersection point in model coordinates. diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index be641b5cd2..7323133876 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -72,7 +72,7 @@ const int32_t BULLET_COLLISION_MASK_DETAILED_RAY = BULLET_COLLISION_GROUP_DETAIL const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; /**jsdoc - *

An entity may collide with the following types of items:

+ *

A collision may be with the following types of items:

* * * @@ -88,7 +88,7 @@ const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; *
ValueDescription
*

The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a * value of 31 means that an entity will collide with all item types.

- * @typedef {number} Entities.CollisionMask + * @typedef {number} CollisionMask */ // The USER collision groups are exposed to script and can be used to generate per-object collision masks. diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 33f6e7278a..8c1c4f56f6 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -13,6 +13,34 @@ class PickFilter { public: + + /**jsdoc + *

A set of flags for a pick filter. The value is constructed by using the | (bitwise OR) operator on the + * individual flag values.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Flag nameValueDescription
PICK_DOMAIN_ENTITIES1Include domain entities when intersecting.
PICK_AVATAR_ENTITIES2Include avatar entities when intersecting.
PICK_LOCAL_ENTITIES4Include local entities when intersecting.
PICK_AVATATRS8Include avatars when intersecting.
PICK_HUD16Include the HUD sphere when intersecting in HMD mode.
PICK_INCLUDE_VISIBLE32Include visible objects when intersecting.
PICK_INCLUDE_INVISIBLE64Include invisible objects when intersecting.
PICK_INCLUDE_COLLIDABLE128Include collidable objects when + * intersecting.
PICK_INCLUDE_NONCOLLIDABLE256Include non-collidable objects when + * intersecting.
PICK_PRECISE512Pick against exact meshes.
PICK_COARSE1024Pick against coarse meshes.
PICK_ALL_INTERSECTIONS2048Return all intersections instead of just the + * closest.
+ * @typedef {number} FilterFlags + */ enum FlagBit { DOMAIN_ENTITIES = 0, AVATAR_ENTITIES, diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index d3fb816f3c..ef2d775eec 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -258,12 +258,12 @@ public: }; /**jsdoc - * A PickRay defines a vector with a starting point. It is used, for example, when finding entities or avatars that lie under a - * mouse click or intersect a laser beam. + * A vector with a starting point. It is used, for example, when finding entities or avatars that lie under a mouse click or + * intersect a laser beam. * * @typedef {object} PickRay - * @property {Vec3} origin - The starting position of the PickRay. - * @property {Vec3} direction - The direction that the PickRay travels. + * @property {Vec3} origin - The starting position of the ray. + * @property {Vec3} direction - The direction that the ray travels. */ class PickRay : public MathPick { public: @@ -291,13 +291,14 @@ QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); /**jsdoc - * A StylusTip defines the tip of a stylus. + * 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} tipOffset - the position offset of the stylus tip. + * @property {number} side - The hand that the stylus is attached to: 0 for left hand, 1 for the + * right hand, -1 for invalid. + * @property {Vec3} tipOffset - The position of the stylus tip relative to the body of the stylus. * @property {Vec3} position - The position of the stylus tip. - * @property {Quat} orientation - The orientation of the stylus tip. + * @property {Quat} orientation - The orientation of the stylus. * @property {Vec3} velocity - The velocity of the stylus tip. */ class StylusTip : public MathPick { @@ -333,12 +334,16 @@ public: }; /**jsdoc -* A PickParabola defines a parabola with a starting point, intitial velocity, and acceleration. +* A parabola defined by a starting point, initial velocity, and acceleration. It is used, for example, when finding entities or +* avatars that intersect a parabolic beam. * * @typedef {object} PickParabola -* @property {Vec3} origin - The starting position of the PickParabola. -* @property {Vec3} velocity - The starting velocity of the parabola. -* @property {Vec3} acceleration - The acceleration that the parabola experiences. +* @property {Vec3} origin - The starting position of the parabola, i.e., the initial position of a virtual projectile whose +* trajectory defines the parabola. +* @property {Vec3} velocity - The starting velocity of the parabola in m/s, i.e., the initial speed of a virtual projectile +* whose trajectory defines the parabola. +* @property {Vec3} acceleration - The acceleration that the parabola experiences in m/s2, i.e., the acceleration of +* a virtual projectile whose trajectory defines the parabola, both magnitude and direction. */ class PickParabola : public MathPick { public: @@ -364,23 +369,6 @@ public: } }; -// TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported. - -/**jsdoc -* A CollisionRegion defines a volume for checking collisions in the physics simulation. - -* @typedef {object} CollisionRegion -* @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined. -* @property {Vec3} position - The position of the collision region, relative to a parent if defined. -* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. -* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. -* The depth is measured in world space, but will scale with the parent if defined. -* @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group -* will be considered colliding with the pick. -* @property {Uuid} parentID - The ID of the parent, either an avatar or an entity. -* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) -* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. -*/ class CollisionRegion : public MathPick { public: CollisionRegion() { } @@ -435,6 +423,29 @@ public: } } + /**jsdoc + * A volume for checking collisions in the physics simulation. + * @typedef {object} CollisionRegion + * @property {Shape} shape - The collision region's shape and size. Dimensions are in world space, but scale with the parent + * if defined. + * @property {boolean} loaded - true if the shape has no model, or has a model and it is loaded. + * @property {Vec3} position - The position of the collision region, relative to the parent if defined. + * @property {Quat} orientation - The orientation of the collision region, relative to the parent if defined. + * @property {number} threshold - The approximate minimum penetration depth for a test object to be considered in contact with + * the collision region. The depth is in world coordinates but scales with the parent if defined. + * @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision + * masks overlap with the regions's collision group are considered to be colliding with the region. + */ + + /**jsdoc + * A physical volume. + * @typedef {object} Shape + * @property {ShapeType} shapeType="none" - The type of shape. + * @property {string} [modelUrl=""] - The model to load to for the shape if shapeType is one of + * "compound", "simple-hull", "simple-compound", or "static-mesh". + * @property {Vec3} dimensions - The dimensions of the shape. + */ + QVariantMap toVariantMap() const override { QVariantMap collisionRegion; @@ -589,7 +600,7 @@ namespace std { } /**jsdoc - *

The type of a collision contact event. + *

The type of a collision contact event.

* * * diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index bf51e455c5..33e4bc7095 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -39,6 +39,8 @@ * sub-meshes. * * + * + * * * *
ValueDescription
"static-mesh"The exact shape of the model.
"plane"A plane.
"ellipsoid"An ellipsoid.
"circle"A circle.
"multisphere"A convex hull generated from a set of spheres.
From 65f9099ea53df06d72aa94917372c70ba2680c1a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 25 Apr 2019 19:29:17 +1200 Subject: [PATCH 04/83] Fix up deprecated notices --- .../src/raypick/PickScriptingInterface.h | 67 ++++++++++++------- .../src/raypick/RayPickScriptingInterface.h | 37 ++++++---- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index e544262b5f..b85249b599 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -32,10 +32,11 @@ * @property {FilterFlags} PICK_HUD - Include the HUD sphere when intersecting in HMD mode. Read-only. * * @property {FilterFlags} PICK_ENTITIES - Include domain and avatar entities when intersecting. Read-only. - *

Deprecated: Use PICK_DOMAIN_ENTITIES | PICK_AVATAR_ENTITIES - * instead.

+ *

Deprecated: This property is deprecated and will be removed. Use PICK_DOMAIN_ENTITIES | + * PICK_AVATAR_ENTITIES instead.

* @property {FilterFlags} PICK_OVERLAYS - Include local entities when intersecting. Read-only. - *

Deprecated: Use PICK_LOCAL_ENTITIES instead.

+ *

Deprecated: This property is deprecated and will be removed. Use PICK_LOCAL_ENTITIES + * instead.

* * @property {FilterFlags} PICK_INCLUDE_VISIBLE - Include visible objects when intersecting. Read-only. *

Warning: Is currently always enabled by default but may not be in the future.

@@ -57,7 +58,8 @@ * @property {IntersectionType} INTERSECTED_LOCAL_ENTITY - Intersected a local entity. Read-only. * @property {IntersectionType} INTERSECTED_OVERLAY - Intersected a local entity. (3D overlays no longer exist.) * Read-only. - *

Deprecated: Use INTERSECTED_LOCAL_ENTITY instead.

+ *

Deprecated: This property is deprecated and will be removed. Use + * INTERSECTED_LOCAL_ENTITY instead.

* @property {IntersectionType} INTERSECTED_AVATAR - Intersected an avatar. Read-only. * @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD sphere. Read-only. * @@ -253,14 +255,16 @@ public slots: /**jsdoc * @function Picks.PICK_ENTITIES - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_DOMAIN_ENTITIES | + * Picks.PICK_AVATAR_ENTITIES properties expression instead. * @returns {number} */ static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } /**jsdoc * @function Picks.PICK_OVERLAYS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_LOCAL_ENTITIES property + * instead. * @returns {number} */ static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } @@ -268,35 +272,39 @@ public slots: /**jsdoc * @function Picks.PICK_DOMAIN_ENTITIES - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_DOMAIN_ENTITIES property + * instead. * @returns {number} */ static constexpr unsigned int PICK_DOMAIN_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); } /**jsdoc * @function Picks.PICK_AVATAR_ENTITIES - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_AVATAR_ENTITIES property + * instead. * @returns {number} */ static constexpr unsigned int PICK_AVATAR_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } /**jsdoc * @function Picks.PICK_LOCAL_ENTITIES - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_LOCAL_ENTITIES property + * instead. * @returns {number} */ static constexpr unsigned int PICK_LOCAL_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } /**jsdoc * @function Picks.PICK_AVATARS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_AVATARS property + * instead. * @returns {number} */ static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); } /**jsdoc * @function Picks.PICK_HUD - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_HUD property instead. * @returns {number} */ static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); } @@ -304,14 +312,16 @@ public slots: /**jsdoc * @function Picks.PICK_INCLUDE_VISIBLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_INCLUDE_VISIBLE property + * instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_VISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); } /**jsdoc * @function Picks.PICK_INCLUDE_INVISIBLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_INCLUDE_INVISIBLE property + * instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); } @@ -319,14 +329,16 @@ public slots: /**jsdoc * @function Picks.PICK_INCLUDE_COLLIDABLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_INCLUDE_COLLIDABLE property + * instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_COLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); } /**jsdoc * @function Picks.PICK_INCLUDE_NONCOLLIDABLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_INCLUDE_NONCOLLIDABLE + * property instead. * @returns {number} */ static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); } @@ -334,14 +346,14 @@ public slots: /**jsdoc * @function Picks.PICK_PRECISE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_PRECISE property instead. * @returns {number} */ static constexpr unsigned int PICK_PRECISE() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); } /**jsdoc * @function Picks.PICK_COARSE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_COARSE property instead. * @returns {number} */ static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } @@ -349,49 +361,56 @@ public slots: /**jsdoc * @function Picks.PICK_ALL_INTERSECTIONS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.PICK_ALL_INTERSECTIONS property + * instead. * @returns {number} */ static constexpr unsigned int PICK_ALL_INTERSECTIONS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ALL_INTERSECTIONS); } /**jsdoc * @function Picks.INTERSECTED_NONE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_NONE property + * instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_NONE() { return IntersectionType::NONE; } /**jsdoc * @function Picks.INTERSECTED_ENTITY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_ENTITY property + * instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_ENTITY() { return IntersectionType::ENTITY; } /**jsdoc * @function Picks.INTERSECTED_LOCAL_ENTITY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_LOCAL_ENTITY + * property instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_LOCAL_ENTITY() { return IntersectionType::LOCAL_ENTITY; } /**jsdoc * @function Picks.INTERSECTED_OVERLAY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_LOCAL_ENTITY + * property instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_OVERLAY() { return INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function Picks.INTERSECTED_AVATAR - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_AVATAR property + * instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_AVATAR() { return IntersectionType::AVATAR; } /**jsdoc * @function Picks.INTERSECTED_HUD - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Picks.INTERSECTED_HUD property + * instead. * @returns {number} */ static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; } diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 32b2b8fb68..a38e953824 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -163,98 +163,107 @@ public slots: /**jsdoc * @function RayPick.PICK_ENTITIES - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the Raypick.PICK_ENTITIES property instead. * @returns {number} */ static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); } /**jsdoc * @function RayPick.PICK_OVERLAYS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_OVERLAYS property instead. * @returns {number} */ static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); } /**jsdoc * @function RayPick.PICK_AVATARS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_AVATARS property instead. * @returns {number} */ static unsigned int PICK_AVATARS() { return PickScriptingInterface::PICK_AVATARS(); } /**jsdoc * @function RayPick.PICK_HUD - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_HUD property instead. * @returns {number} */ static unsigned int PICK_HUD() { return PickScriptingInterface::PICK_HUD(); } /**jsdoc * @function RayPick.PICK_COARSE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_COARSE property instead. * @returns {number} */ static unsigned int PICK_COARSE() { return PickScriptingInterface::PICK_COARSE(); } /**jsdoc * @function RayPick.PICK_INCLUDE_INVISIBLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_INCLUDE_INVISIBLE + * property instead. * @returns {number} */ static unsigned int PICK_INCLUDE_INVISIBLE() { return PickScriptingInterface::PICK_INCLUDE_INVISIBLE(); } /**jsdoc * @function RayPick.PICK_INCLUDE_NONCOLLIDABLE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_INCLUDE_NONCOLLIDABLE + * property instead. * @returns {number} */ static unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE(); } /**jsdoc * @function RayPick.PICK_ALL_INTERSECTIONS - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.PICK_ALL_INTERSECTIONS + * property instead. * @returns {number} */ static unsigned int PICK_ALL_INTERSECTIONS() { return PickScriptingInterface::PICK_ALL_INTERSECTIONS(); } /**jsdoc * @function RayPick.INTERSECTED_NONE - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_NONE property + * instead. * @returns {number} */ static unsigned int INTERSECTED_NONE() { return PickScriptingInterface::INTERSECTED_NONE(); } /**jsdoc * @function RayPick.INTERSECTED_ENTITY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_ENTITY property + * instead. * @returns {number} */ static unsigned int INTERSECTED_ENTITY() { return PickScriptingInterface::INTERSECTED_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_OVERLAY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_LOCAL_ENTITY + * property instead. * @returns {number} */ static unsigned int INTERSECTED_LOCAL_ENTITY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_OVERLAY - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_OVERLAY property + * instead. * @returns {number} */ static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_LOCAL_ENTITY(); } /**jsdoc * @function RayPick.INTERSECTED_AVATAR - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_AVATAR property + * instead. * @returns {number} */ static unsigned int INTERSECTED_AVATAR() { return PickScriptingInterface::INTERSECTED_AVATAR(); } /**jsdoc * @function RayPick.INTERSECTED_HUD - * @deprecated This function is deprecated and will be removed. Use the property of the same name instead. + * @deprecated This function is deprecated and will be removed. Use the RayPick.INTERSECTED_HUD property + * instead. * @returns {number} */ static unsigned int INTERSECTED_HUD() { return PickScriptingInterface::INTERSECTED_HUD(); } From ca5c7e3904a9f6d150a35d10b16f70d84d5a7106 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 26 Apr 2019 12:56:01 -0700 Subject: [PATCH 05/83] runtime switch for deferred/forward rendering --- interface/src/Application.cpp | 5 +- interface/src/SecondaryCamera.cpp | 4 +- interface/src/SecondaryCamera.h | 2 +- interface/src/graphics/GraphicsEngine.cpp | 6 +- interface/src/graphics/GraphicsEngine.h | 2 +- libraries/render-utils/src/RenderViewTask.cpp | 18 +-- libraries/render-utils/src/RenderViewTask.h | 5 +- libraries/task/src/task/Config.cpp | 8 ++ libraries/task/src/task/Config.h | 12 ++ libraries/task/src/task/Task.h | 108 ++++++++++++++---- 10 files changed, 129 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e2e4e8de1..d0fd406842 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -243,11 +243,8 @@ #include "webbrowser/WebBrowserSuggestionsEngine.h" #include - #include "AboutUtil.h" -#include - #if defined(Q_OS_WIN) #include @@ -2979,7 +2976,7 @@ void Application::initializeDisplayPlugins() { void Application::initializeRenderEngine() { // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { - _graphicsEngine.initializeRender(DISABLE_DEFERRED); + _graphicsEngine.initializeRender(); DependencyManager::get()->registerKeyboardHighlighting(); }); } diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index da2874a3f4..704d7963e7 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -272,10 +272,10 @@ public: } }; -void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { +void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) { const auto cachedArg = task.addJob("SecondaryCamera"); - task.addJob("RenderSecondView", cullFunctor, isDeferred, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); + task.addJob("RenderSecondView", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1); task.addJob("EndSecondaryCamera", cachedArg); } \ No newline at end of file diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index 941ccc5f93..463034527a 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -65,7 +65,7 @@ public: using JobModel = render::Task::Model; SecondaryCameraRenderTask() {} void configure(const Config& config) {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); }; #endif diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index 267822baf2..4f8b86cc0c 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -65,15 +65,15 @@ void GraphicsEngine::initializeGPU(GLWidget* glwidget) { DependencyManager::get()->setGPUContext(_gpuContext); } -void GraphicsEngine::initializeRender(bool disableDeferred) { +void GraphicsEngine::initializeRender() { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; _renderEngine->addJob("UpdateScene"); #ifndef Q_OS_ANDROID - _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !disableDeferred); + _renderEngine->addJob("SecondaryCameraJob", cullFunctor); #endif - _renderEngine->addJob("RenderMainView", cullFunctor, !disableDeferred, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); + _renderEngine->addJob("RenderMainView", cullFunctor, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); _renderEngine->load(); _renderEngine->registerScene(_renderScene); diff --git a/interface/src/graphics/GraphicsEngine.h b/interface/src/graphics/GraphicsEngine.h index f0b88d2459..dccb098d5e 100644 --- a/interface/src/graphics/GraphicsEngine.h +++ b/interface/src/graphics/GraphicsEngine.h @@ -44,7 +44,7 @@ public: ~GraphicsEngine(); void initializeGPU(GLWidget*); - void initializeRender(bool disableDeferred); + void initializeRender(); void startup(); void shutdown(); diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 7230635060..61823cf01c 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -15,7 +15,9 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) { +#include + +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); assert(items.canCast()); @@ -25,17 +27,19 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: // Assemble the lighting stages current frames const auto lightingStageFramesAndZones = task.addJob("AssembleStages", items); - if (isDeferred) { + if (!DISABLE_DEFERRED) { // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling // is performed, then casters not in the view frustum will be removed, which is not what we wish. const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); - const auto shadowTaskOut = task.addJob("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); + const auto shadowTaskOut = task.addSwitchJob("RenderShadowTask", 0, shadowTaskIn, cullFunctor, tagBits, tagMask); - const auto renderInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); - task.addJob("RenderDeferredTask", renderInput); + const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); + task.addSwitchJob("RenderDeferredTask", 0, renderDeferredInput); + + const auto renderForwardInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); + task.addSwitchJob("RenderForwardTask", 1, renderForwardInput); } else { const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); - task.addJob("Forward", renderInput); + task.addJob("RenderForwardTask", renderInput); } } - diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index 5da3d18474..b4d9326944 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -19,11 +19,12 @@ class RenderViewTask { public: using Input = RenderFetchCullSortTask::Output; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelIS; RenderViewTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); + void configure(const render::SwitchConfig& configuration) {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/task/src/task/Config.cpp b/libraries/task/src/task/Config.cpp index 5e8e4b246d..c836d06717 100644 --- a/libraries/task/src/task/Config.cpp +++ b/libraries/task/src/task/Config.cpp @@ -133,3 +133,11 @@ JobConfig* TaskConfig::getJobConfig(const std::string& jobPath) const { return found; } } + +void SwitchConfig::setSwitchIndex(uint8_t index) { + if (_switchIndex != index) { + _switchIndex = index; + // We can re-use this signal here + emit dirtyEnabled(); + } +} \ No newline at end of file diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index 486b28b6b9..d158e16eec 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -262,6 +262,18 @@ public slots: void refresh(); }; +class SwitchConfig : public TaskConfig { + Q_OBJECT + Q_PROPERTY(bool switchIndex READ getSwitchIndex WRITE setSwitchIndex NOTIFY dirtyEnabled) + +public: + uint8_t getSwitchIndex() const { return _switchIndex; } + void setSwitchIndex(uint8_t index); + +protected: + uint8_t _switchIndex { 0 }; +}; + } #endif // hifi_task_Config_h diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index b74986235e..e4f7e789f4 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -15,6 +15,8 @@ #include "Config.h" #include "Varying.h" +#include + namespace task { class JobConcept; @@ -244,25 +246,6 @@ public: const Varying getOutput() const override { return _output; } Varying& editInput() override { return _input; } - typename Jobs::iterator editJob(std::string name) { - typename Jobs::iterator jobIt; - for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) { - if (jobIt->getName() == name) { - return jobIt; - } - } - return jobIt; - } - typename Jobs::const_iterator getJob(std::string name) const { - typename Jobs::const_iterator jobIt; - for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) { - if (jobIt->getName() == name) { - return jobIt; - } - } - return jobIt; - } - TaskConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {} // Create a new job in the container's queue; returns the job's output @@ -331,7 +314,7 @@ public: return Concept::_config; } - void applyConfiguration() override { + virtual void applyConfiguration() override { TimeProfiler probe("configure::" + JobConcept::getName()); jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); for (auto& job : TaskConcept::_jobs) { @@ -339,7 +322,7 @@ public: } } - void run(const ContextPointer& jobContext) override { + virtual void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { for (auto job : TaskConcept::_jobs) { @@ -357,6 +340,81 @@ public: template using ModelO = TaskModel; template using ModelIO = TaskModel; + template class SwitchTaskModel : public TaskModel { + public: + using Input = I; + + std::unordered_map _jobsSwitch; + + SwitchTaskModel(const std::string& name, const Varying& input, QConfigPointer config) : TaskModel(name, input, config) {} + + template + static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + auto model = std::make_shared(name, input, std::make_shared()); + + { + TimeProfiler probe("build::" + model->getName()); + model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); + } + // Recreate the Config to use the templated type + model->createConfiguration(); + model->applyConfiguration(); + + return model; + } + + template + static std::shared_ptr create(const std::string& name, A&&... args) { + const auto input = Varying(Input()); + return create(name, input, std::forward(args)...); + } + + void applyConfiguration() override { + TaskModel::applyConfiguration(); + + for (auto& jobs : _jobsSwitch) { + for (auto& job : jobs.second) { + job.applyConfiguration(); + } + } + } + + void run(const ContextPointer& jobContext) override { + auto config = std::static_pointer_cast(Concept::_config); + if (config->isEnabled()) { + // First we run all the setup jobs + TaskModel::run(jobContext); + + // Then we run the branching jobs + auto jobsIt = _jobsSwitch.find(config->getSwitchIndex()); + if (jobsIt != _jobsSwitch.end()) { + for (auto job : jobsIt->second) { + job.run(jobContext); + if (jobContext->taskFlow.doAbortTask()) { + jobContext->taskFlow.reset(); + return; + } + } + } + } + } + + template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, NA&&... args) { + auto& jobs = _jobsSwitch[index]; + jobs.emplace_back((NT::JobModel::create(name, input, std::forward(args)...))); + + // Conect the child config to this task's config + std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(jobs.back().getConfiguration(), name); + + return jobs.back().getOutput(); + } + template const Varying addSwitchJob(std::string name, uint8_t index, NA&&... args) { + const auto input = Varying(typename NT::JobModel::Input()); + return addJob(name, index, input, std::forward(args)...); + } + }; + template using ModelIS = SwitchTaskModel; + // Create a new job in the Task's queue; returns the job's output template const Varying addJob(std::string name, const Varying& input, A&&... args) { return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); @@ -365,6 +423,13 @@ public: const auto input = Varying(typename T::JobModel::Input()); return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); } + template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, A&&... args) { + return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); + } + template const Varying addSwitchJob(std::string name, uint8_t index, A&&... args) { + const auto input = Varying(typename T::JobModel::Input()); + return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); + } std::shared_ptr getConfiguration() { return std::static_pointer_cast(JobType::_concept->getConfiguration()); @@ -407,6 +472,7 @@ protected: #define Task_DeclareTypeAliases(ContextType, TimeProfiler) \ using JobConfig = task::JobConfig; \ using TaskConfig = task::TaskConfig; \ + using SwitchConfig = task::SwitchConfig; \ template using PersistentConfig = task::PersistentConfig; \ using Job = task::Job; \ using Task = task::Task; \ From 4a02ab4fe2e20f1ba179017546e7ee3a7da3ec4e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 29 Apr 2019 14:01:09 -0700 Subject: [PATCH 06/83] move switch logic around --- libraries/render-utils/src/RenderViewTask.cpp | 34 ++-- libraries/render-utils/src/RenderViewTask.h | 27 ++- libraries/task/src/task/Config.cpp | 14 +- libraries/task/src/task/Config.h | 32 ++-- libraries/task/src/task/Task.h | 174 ++++++++++++------ 5 files changed, 187 insertions(+), 94 deletions(-) diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 61823cf01c..a38769165b 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -10,16 +10,34 @@ // #include "RenderViewTask.h" -#include "AssembleLightingStageTask.h" #include "RenderShadowTask.h" #include "RenderDeferredTask.h" #include "RenderForwardTask.h" #include +void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { + const auto items = input.getN(0); + const auto lightingModel = input.getN(1); + const auto lightingStageFramesAndZones = input.getN(2); + + // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling + // is performed, then casters not in the view frustum will be removed, which is not what we wish. + const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); + const auto shadowTaskOut = task.addJob("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); + + const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); + task.addJob("RenderDeferredTask", renderDeferredInput); +} + +void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { + task.addBranch("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask); + + task.addBranch("RenderForwardTask", 1, input); +} + void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { const auto items = task.addJob("FetchCullSort", cullFunctor, tagBits, tagMask); - assert(items.canCast()); // Issue the lighting model, aka the big global settings for the view const auto lightingModel = task.addJob("LightingModel"); @@ -28,16 +46,8 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: const auto lightingStageFramesAndZones = task.addJob("AssembleStages", items); if (!DISABLE_DEFERRED) { - // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling - // is performed, then casters not in the view frustum will be removed, which is not what we wish. - const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); - const auto shadowTaskOut = task.addSwitchJob("RenderShadowTask", 0, shadowTaskIn, cullFunctor, tagBits, tagMask); - - const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); - task.addSwitchJob("RenderDeferredTask", 0, renderDeferredInput); - - const auto renderForwardInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); - task.addSwitchJob("RenderForwardTask", 1, renderForwardInput); + const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); + task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); } else { const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("RenderForwardTask", renderInput); diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index b4d9326944..cdb56a2189 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -15,15 +15,38 @@ #include #include +#include "AssembleLightingStageTask.h" + +class RenderShadowsAndDeferredTask { +public: + using Input = render::VaryingSet3; + using JobModel = render::Task::ModelI; + + RenderShadowsAndDeferredTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); + +}; + +class DeferredForwardSwitchJob { +public: + using Input = render::VaryingSet3; + using JobModel = render::Switch::ModelI; + + DeferredForwardSwitchJob() {} + + void configure(const render::SwitchConfig& config) {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); + +}; class RenderViewTask { public: using Input = RenderFetchCullSortTask::Output; - using JobModel = render::Task::ModelIS; + using JobModel = render::Task::ModelI; RenderViewTask() {} - void configure(const render::SwitchConfig& configuration) {} void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/task/src/task/Config.cpp b/libraries/task/src/task/Config.cpp index c836d06717..79e0b9f7fb 100644 --- a/libraries/task/src/task/Config.cpp +++ b/libraries/task/src/task/Config.cpp @@ -38,7 +38,7 @@ void JobConfig::setPresetList(const QJsonObject& object) { } } -void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::string& name) { +void JobConfig::connectChildConfig(std::shared_ptr childConfig, const std::string& name) { childConfig->setParent(this); childConfig->setObjectName(name.c_str()); @@ -52,7 +52,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin } } -void TaskConfig::transferChildrenConfigs(QConfigPointer source) { +void JobConfig::transferChildrenConfigs(std::shared_ptr source) { if (!source) { return; } @@ -70,13 +70,13 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) { } } -void TaskConfig::refresh() { +void JobConfig::refresh() { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "refresh"); return; } - _task->applyConfiguration(); + _jobConcept->applyConfiguration(); } TaskConfig* TaskConfig::getRootConfig(const std::string& jobPath, std::string& jobName) const { @@ -134,9 +134,9 @@ JobConfig* TaskConfig::getJobConfig(const std::string& jobPath) const { } } -void SwitchConfig::setSwitchIndex(uint8_t index) { - if (_switchIndex != index) { - _switchIndex = index; +void SwitchConfig::setBranch(uint8_t branch) { + if (_branch != branch) { + _branch = branch; // We can re-use this signal here emit dirtyEnabled(); } diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index d158e16eec..891970006d 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -154,6 +154,11 @@ public: */ Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; } + void connectChildConfig(std::shared_ptr childConfig, const std::string& name); + void transferChildrenConfigs(std::shared_ptr source); + + JobConcept* _jobConcept; + public slots: /**jsdoc @@ -162,6 +167,11 @@ public slots: */ void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } + /**jsdoc + * @function Render.refresh + */ + void refresh(); + signals: /**jsdoc @@ -248,30 +258,18 @@ public: auto subs = getSubConfigs(); return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]); } - - void connectChildConfig(QConfigPointer childConfig, const std::string& name); - void transferChildrenConfigs(QConfigPointer source); - - JobConcept* _task; - -public slots: - - /**jsdoc - * @function Render.refresh - */ - void refresh(); }; -class SwitchConfig : public TaskConfig { +class SwitchConfig : public JobConfig { Q_OBJECT - Q_PROPERTY(bool switchIndex READ getSwitchIndex WRITE setSwitchIndex NOTIFY dirtyEnabled) + Q_PROPERTY(bool branch READ getBranch WRITE setBranch NOTIFY dirtyEnabled) public: - uint8_t getSwitchIndex() const { return _switchIndex; } - void setSwitchIndex(uint8_t index); + uint8_t getBranch() const { return _branch; } + void setBranch(uint8_t index); protected: - uint8_t _switchIndex { 0 }; + uint8_t _branch { 0 }; }; } diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index e4f7e789f4..87fb21d59b 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -177,6 +177,7 @@ public: template using ModelO = Model; template using ModelIO = Model; + Job() {} Job(const ConceptPointer& concept) : _concept(concept) {} virtual ~Job() = default; @@ -304,7 +305,7 @@ public: // swap Concept::_config = config; // Capture this - std::static_pointer_cast(Concept::_config)->_task = this; + Concept::_config->_jobConcept = this; } QConfigPointer& getConfiguration() override { @@ -314,7 +315,7 @@ public: return Concept::_config; } - virtual void applyConfiguration() override { + void applyConfiguration() override { TimeProfiler probe("configure::" + JobConcept::getName()); jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); for (auto& job : TaskConcept::_jobs) { @@ -322,7 +323,7 @@ public: } } - virtual void run(const ContextPointer& jobContext) override { + void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { for (auto job : TaskConcept::_jobs) { @@ -340,17 +341,86 @@ public: template using ModelO = TaskModel; template using ModelIO = TaskModel; - template class SwitchTaskModel : public TaskModel { + // Create a new job in the Task's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, A&&... args) { + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + } + template const Varying addJob(std::string name, A&&... args) { + const auto input = Varying(typename T::JobModel::Input()); + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + } + + std::shared_ptr getConfiguration() { + return std::static_pointer_cast(JobType::_concept->getConfiguration()); + } +}; + + +// A Switch is a specialized job to run a collection of other jobs and switch between different branches at run time +// It can be created on any type T by aliasing the type JobModel in the class T +// using JobModel = Switch::Model +// The class T is expected to have a "build" method acting as a constructor. +// The build method is where child Jobs can be added internally to the branches of the switch +// where the input of the switch can be setup to feed the child jobs +// and where the output of the switch is defined +template +class Switch : public Job { +public: + using Context = JC; + using TimeProfiler = TP; + using ContextPointer = std::shared_ptr; + using Config = SwitchConfig; + using JobType = Job; + using None = typename JobType::None; + using Concept = typename JobType::Concept; + using ConceptPointer = typename JobType::ConceptPointer; + using Branches = std::unordered_map; + + Switch(ConceptPointer concept) : JobType(concept) {} + + class SwitchConcept : public Concept { public: + Varying _input; + Varying _output; + Branches _branches; + + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } + Varying& editInput() override { return _input; } + + SwitchConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {} + + template const Varying addBranch(std::string name, uint8_t index, const Varying& input, NA&&... args) { + auto& branch = _branches[index]; + branch = JobType(NT::JobModel::create(name, input, std::forward(args)...)); + + // Conect the child config to this task's config + std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(branch.getConfiguration(), name); + + return branch.getOutput(); + } + template const Varying addBranch(std::string name, uint8_t index, NA&&... args) { + const auto input = Varying(typename NT::JobModel::Input()); + return addBranch(name, index, input, std::forward(args)...); + } + }; + + template class SwitchModel : public SwitchConcept { + public: + using Data = T; using Input = I; + using Output = O; - std::unordered_map _jobsSwitch; + Data _data; - SwitchTaskModel(const std::string& name, const Varying& input, QConfigPointer config) : TaskModel(name, input, config) {} + SwitchModel(const std::string& name, const Varying& input, QConfigPointer config) : + SwitchConcept(name, input, config), + _data(Data()) { + } template - static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { - auto model = std::make_shared(name, input, std::make_shared()); + static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + auto model = std::make_shared(name, input, std::make_shared()); { TimeProfiler probe("build::" + model->getName()); @@ -364,78 +434,69 @@ public: } template - static std::shared_ptr create(const std::string& name, A&&... args) { + static std::shared_ptr create(const std::string& name, A&&... args) { const auto input = Varying(Input()); return create(name, input, std::forward(args)...); } - void applyConfiguration() override { - TaskModel::applyConfiguration(); + void createConfiguration() { + // A brand new config + auto config = std::make_shared(); + // Make sure we transfer the former children configs to the new config + config->transferChildrenConfigs(Concept::_config); + // swap + Concept::_config = config; + // Capture this + Concept::_config->_jobConcept = this; + } - for (auto& jobs : _jobsSwitch) { - for (auto& job : jobs.second) { - job.applyConfiguration(); - } + QConfigPointer& getConfiguration() override { + if (!Concept::_config) { + createConfiguration(); + } + return Concept::_config; + } + + void applyConfiguration() override { + TimeProfiler probe("configure::" + JobConcept::getName()); + jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); + for (auto& branch : _branches) { + branch.second.applyConfiguration(); } } void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { - // First we run all the setup jobs - TaskModel::run(jobContext); - - // Then we run the branching jobs - auto jobsIt = _jobsSwitch.find(config->getSwitchIndex()); - if (jobsIt != _jobsSwitch.end()) { - for (auto job : jobsIt->second) { - job.run(jobContext); - if (jobContext->taskFlow.doAbortTask()) { - jobContext->taskFlow.reset(); - return; - } + auto jobsIt = _branches.find(config->getBranch()); + if (jobsIt != _branches.end()) { + jobsIt->second.run(jobContext); + if (jobContext->taskFlow.doAbortTask()) { + jobContext->taskFlow.reset(); + return; } } } } - - template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, NA&&... args) { - auto& jobs = _jobsSwitch[index]; - jobs.emplace_back((NT::JobModel::create(name, input, std::forward(args)...))); - - // Conect the child config to this task's config - std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(jobs.back().getConfiguration(), name); - - return jobs.back().getOutput(); - } - template const Varying addSwitchJob(std::string name, uint8_t index, NA&&... args) { - const auto input = Varying(typename NT::JobModel::Input()); - return addJob(name, index, input, std::forward(args)...); - } }; - template using ModelIS = SwitchTaskModel; + template using Model = SwitchModel; + template using ModelI = SwitchModel; + // TODO: Switches don't support Outputs yet + //template using ModelO = SwitchModel; + //template using ModelIO = SwitchModel; - // Create a new job in the Task's queue; returns the job's output - template const Varying addJob(std::string name, const Varying& input, A&&... args) { - return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); + // Create a new job in the Switches' branches; returns the job's output + template const Varying addBranch(std::string name, uint8_t index, const Varying& input, A&&... args) { + return std::static_pointer_cast(JobType::_concept)->template addBranch(name, index, input, std::forward(args)...); } - template const Varying addJob(std::string name, A&&... args) { + template const Varying addBranch(std::string name, uint8_t index, A&&... args) { const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); - } - template const Varying addSwitchJob(std::string name, uint8_t index, const Varying& input, A&&... args) { - return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); - } - template const Varying addSwitchJob(std::string name, uint8_t index, A&&... args) { - const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast(JobType::_concept)->template addSwitchJob(name, index, input, std::forward(args)...); + return std::static_pointer_cast(JobType::_concept)->template addBranch(name, index, input, std::forward(args)...); } std::shared_ptr getConfiguration() { return std::static_pointer_cast(JobType::_concept->getConfiguration()); } - -protected: }; template @@ -475,6 +536,7 @@ protected: using SwitchConfig = task::SwitchConfig; \ template using PersistentConfig = task::PersistentConfig; \ using Job = task::Job; \ + using Switch = task::Switch; \ using Task = task::Task; \ using Engine = task::Engine; \ using Varying = task::Varying; \ From 638e040e17d51177253c1bd6c96079ad3a9bce5a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 30 Apr 2019 12:13:08 +1200 Subject: [PATCH 07/83] Quat JSDoc update --- libraries/script-engine/src/Quat.h | 70 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index d24db786d0..0a5e58ac26 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -23,7 +23,7 @@ #include /**jsdoc - * A quaternion value. See also the {@link Quat(0)|Quat} object. + * A quaternion value. See also the {@link Quat(0)|Quat} API. * @typedef {object} Quat * @property {number} x - Imaginary component i. * @property {number} y - Imaginary component j. @@ -32,9 +32,10 @@ */ /**jsdoc - * The Quat API provides facilities for generating and manipulating quaternions. + * 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 * @@ -59,7 +60,7 @@ class Quat : public QObject, protected QScriptable { public slots: /**jsdoc - * Multiply two quaternions. + * Multiplies two quaternions. * @function Quat(0).multiply * @param {Quat} q1 - The first quaternion. * @param {Quat} q2 - The second quaternion. @@ -90,8 +91,8 @@ public slots: glm::quat normalize(const glm::quat& q); /**jsdoc - * Calculate the conjugate of a quaternion. For a unit quaternion, its conjugate is the same as its - * {@link Quat(0).inverse|Quat.inverse}. + * Calculates 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 of q. @@ -106,8 +107,9 @@ public slots: 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. + * Calculates a camera orientation given an 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} target - The point to look at. @@ -121,7 +123,7 @@ public slots: 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 + * Calculates a camera orientation given an 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. @@ -137,7 +139,7 @@ public slots: glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); /**jsdoc - * Calculate the shortest rotation from a first vector onto a second. + * Calculates 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. @@ -154,7 +156,7 @@ public slots: glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); /**jsdoc - * Generate a quaternion from a {@link Vec3} of Euler angles in degrees. + * Generates 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, the angles being the rotations about the x, y, and z * axes. @@ -168,7 +170,7 @@ public slots: glm::quat fromVec3Degrees(const glm::vec3& vec3); /**jsdoc - * Generate a quaternion from a {@link Vec3} of Euler angles in radians. + * Generates 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, the angles being the rotations about the x, y, and z * axes. @@ -179,7 +181,7 @@ public slots: glm::quat fromVec3Radians(const glm::vec3& vec3); /**jsdoc - * Generate a quaternion from pitch, yaw, and roll values in degrees. + * Generates 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. @@ -191,7 +193,7 @@ public slots: glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); /**jsdoc - * Generate a quaternion from pitch, yaw, and roll values in radians. + * Generates 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. @@ -203,7 +205,7 @@ public slots: glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); /**jsdoc - * Calculate the inverse of a quaternion. For a unit quaternion, its inverse is the same as its + * Calculates 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. @@ -219,9 +221,9 @@ public slots: glm::quat inverse(const glm::quat& q); /**jsdoc - * Get the "front" direction that the camera would face if its orientation was set to the quaternion value. + * Gets 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. + * 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. @@ -229,9 +231,9 @@ public slots: glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } /**jsdoc - * Get the "forward" direction that the camera would face if its orientation was set to the quaternion value. + * Gets 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. + * 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. @@ -242,8 +244,8 @@ public slots: glm::vec3 getForward(const glm::quat& orientation); /**jsdoc - * 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. + * Gets 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. @@ -251,8 +253,8 @@ public slots: glm::vec3 getRight(const glm::quat& orientation); /**jsdoc - * 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. + * Gets 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. @@ -260,8 +262,8 @@ public slots: glm::vec3 getUp(const glm::quat& orientation); /**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 with pitches around +/-90 degrees.) + * Calculates 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 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, the angles being the @@ -273,7 +275,7 @@ public slots: glm::vec3 safeEulerAngles(const glm::quat& orientation); /**jsdoc - * Generate a quaternion given an angle to rotate through and an axis to rotate about. + * Generates 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 unit axis to rotate about. @@ -286,7 +288,7 @@ public slots: glm::quat angleAxis(float angle, const glm::vec3& v); /**jsdoc - * Get the rotation axis for a quaternion. + * Gets the rotation axis for a quaternion. * @function Quat(0).axis * @param {Quat} q - The quaternion. * @returns {Vec3} The normalized rotation axis for q. @@ -300,7 +302,7 @@ public slots: glm::vec3 axis(const glm::quat& orientation); /**jsdoc - * Get the rotation angle for a quaternion. + * Gets the rotation angle for a quaternion. * @function Quat(0).angle * @param {Quat} q - The quaternion. * @returns {number} The rotation angle for q, in radians. WARNING: This value is in radians @@ -316,7 +318,7 @@ public slots: // 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. + * Computes a spherical linear interpolation between two rotations, safely handling two rotations that are very similar. * See also, {@link Quat(0).slerp|Quat.slerp}. * @function Quat(0).mix * @param {Quat} q1 - The beginning rotation. @@ -336,7 +338,7 @@ public slots: glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); /**jsdoc - * Compute a spherical linear interpolation between two rotations, for rotations that are not very similar. + * Computes 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. @@ -349,7 +351,7 @@ public slots: glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); /**jsdoc - * Compute a spherical quadrangle interpolation between two rotations along a path oriented toward two other rotations. + * Computes 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 - Initial rotation. @@ -364,8 +366,8 @@ public slots: 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. 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. + * Calculates 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. @@ -385,7 +387,7 @@ public slots: 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. + * Prints 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. @@ -403,7 +405,7 @@ public slots: 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 + * Tests 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 {@link Quat(0).dot|Quat.dot} and test for closeness to +/-1. * @function Quat(0).equal * @param {Quat} q1 - The first quaternion. From 7db1e3372051c92b891e438a59bca9cbb2ec1db6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 30 Apr 2019 12:13:43 +1200 Subject: [PATCH 08/83] Note camera axes in Camera JSDoc --- interface/src/FancyCamera.h | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index aead54d0fd..0cfe147138 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -20,6 +20,7 @@ class FancyCamera : public Camera { /**jsdoc * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. + * The High Fidelity camera has axes x = right, y = up, -z = forward. * * @namespace Camera * From 0b3f2b1765b8dd53c707ce38b1eca3f5f79c8a88 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 30 Apr 2019 14:48:48 -0700 Subject: [PATCH 09/83] Fix crash due to invalid indices in baker::calculateNormals --- .../model-baker/CalculateBlendshapeNormalsTask.cpp | 2 +- .../src/model-baker/CalculateMeshNormalsTask.cpp | 2 +- libraries/model-baker/src/model-baker/ModelMath.h | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/model-baker/src/model-baker/CalculateBlendshapeNormalsTask.cpp b/libraries/model-baker/src/model-baker/CalculateBlendshapeNormalsTask.cpp index b908fa9ced..e09bf7a419 100644 --- a/libraries/model-baker/src/model-baker/CalculateBlendshapeNormalsTask.cpp +++ b/libraries/model-baker/src/model-baker/CalculateBlendshapeNormalsTask.cpp @@ -61,7 +61,7 @@ void CalculateBlendshapeNormalsTask::run(const baker::BakeContextPointer& contex outVertex = blendshape.vertices[lookupIndex]; } else { // Index isn't in the blendshape, so return vertex from mesh - outVertex = mesh.vertices[lookupIndex]; + outVertex = baker::safeGet(mesh.vertices, lookupIndex); } }); } diff --git a/libraries/model-baker/src/model-baker/CalculateMeshNormalsTask.cpp b/libraries/model-baker/src/model-baker/CalculateMeshNormalsTask.cpp index a6884e104d..f07fb6fcd1 100644 --- a/libraries/model-baker/src/model-baker/CalculateMeshNormalsTask.cpp +++ b/libraries/model-baker/src/model-baker/CalculateMeshNormalsTask.cpp @@ -32,7 +32,7 @@ void CalculateMeshNormalsTask::run(const baker::BakeContextPointer& context, con return &normalsOut[normalIndex]; }, [&mesh](int vertexIndex, glm::vec3& outVertex) /* VertexSetter */ { - outVertex = mesh.vertices[vertexIndex]; + outVertex = baker::safeGet(mesh.vertices, vertexIndex); } ); } diff --git a/libraries/model-baker/src/model-baker/ModelMath.h b/libraries/model-baker/src/model-baker/ModelMath.h index 38bb3e1b3d..bbf42ffd9c 100644 --- a/libraries/model-baker/src/model-baker/ModelMath.h +++ b/libraries/model-baker/src/model-baker/ModelMath.h @@ -25,6 +25,17 @@ namespace baker { } } + template + const T& safeGet(const QVector& data, int i) { + static T t; + + if (i >= 0 && data.size() > i) { + return data[i]; + } else { + return t; + } + } + // Returns a reference to the normal at the specified index, or nullptr if it cannot be accessed using NormalAccessor = std::function; From fb77928652409222f5105f86e837e02aef04d1d0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 May 2019 15:29:04 -0700 Subject: [PATCH 10/83] Private user data is accessible to all right now --- libraries/entities/src/EntityItem.cpp | 18 ++++++++++++++++++ libraries/entities/src/EntityItem.h | 4 ++++ .../entities/src/EntityItemProperties.cpp | 13 +++++++++++++ libraries/entities/src/EntityItemProperties.h | 2 ++ .../src/EntityItemPropertiesDefaults.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + libraries/entities/src/EntityTree.cpp | 8 ++++++++ libraries/networking/src/udt/PacketHeaders.h | 1 + 8 files changed, 48 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e1ede9192a..dc871e655f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -276,6 +276,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, getPrivateUserData()); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); @@ -812,6 +813,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); + READ_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); { // When we own the simulation we don't accept updates to the entity's transform/velocities @@ -1331,6 +1333,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(privateUserData, getPrivateUserData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription); COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getLocalPosition); @@ -1479,6 +1482,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(privateUserData, setPrivateUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition); @@ -3120,6 +3124,20 @@ void EntityItem::setUserData(const QString& value) { }); } +QString EntityItem::getPrivateUserData() const { + QString result; + withReadLock([&] { + result = _privateUserData; + }); + return result; +} + +void EntityItem::setPrivateUserData(const QString& value) { + withWriteLock([&] { + _privateUserData = value; + }); +} + // Certifiable Properties #define DEFINE_PROPERTY_GETTER(type, accessor, var) \ type EntityItem::get##accessor() const { \ diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 29a1a8d73c..90a1d82253 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -331,6 +331,9 @@ public: QString getUserData() const; virtual void setUserData(const QString& value); // FIXME: This is suspicious + QString getPrivateUserData() const; + void setPrivateUserData(const QString& value); + // FIXME not thread safe? const SimulationOwner& getSimulationOwner() const { return _simulationOwner; } void setSimulationOwner(const QUuid& id, uint8_t priority); @@ -641,6 +644,7 @@ protected: bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC }; bool _locked { ENTITY_ITEM_DEFAULT_LOCKED }; QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA }; + QString _privateUserData{ ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA }; SimulationOwner _simulationOwner; bool _shouldHighlight { false }; QString _name { ENTITY_ITEM_DEFAULT_NAME }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 2b738bc4e7..86f293f2ea 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -487,6 +487,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); + CHECK_PROPERTY_CHANGE(PROP_PRIVATE_USER_DATA, privateUserData); CHECK_PROPERTY_CHANGE(PROP_HREF, href); CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); CHECK_PROPERTY_CHANGE(PROP_POSITION, position); @@ -818,6 +819,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * which you can manipulate the properties of, and use JSON.stringify() to convert the object into a string to * put in the property. * + * @property {string} privateUserData="" - Like userData, but only accessible by the Entity Server Scripts. + * * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. * @property {number} scriptTimestamp=0 - Intended to be used to indicate when the client entity script was loaded. Should be * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). @@ -1591,6 +1594,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PRIVATE_USER_DATA, privateUserData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position); @@ -1999,6 +2003,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked); COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); + COPY_PROPERTY_FROM_QSCRIPTVALUE(privateUserData, QString, setPrivateUserData); COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); COPY_PROPERTY_FROM_QSCRIPTVALUE(position, vec3, setPosition); @@ -2288,6 +2293,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(name); COPY_PROPERTY_IF_CHANGED(locked); COPY_PROPERTY_IF_CHANGED(userData); + COPY_PROPERTY_IF_CHANGED(privateUserData); COPY_PROPERTY_IF_CHANGED(href); COPY_PROPERTY_IF_CHANGED(description); COPY_PROPERTY_IF_CHANGED(position); @@ -2573,6 +2579,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_LOCKED, Locked, locked, bool); ADD_PROPERTY_TO_MAP(PROP_USER_DATA, UserData, userData, QString); + ADD_PROPERTY_TO_MAP(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); @@ -3047,6 +3054,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, properties.getPrivateUserData()); APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); @@ -3530,6 +3538,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition); @@ -3941,6 +3950,7 @@ void EntityItemProperties::markAllChanged() { _nameChanged = true; _lockedChanged = true; _userDataChanged = true; + _privateUserDataChanged = true; _hrefChanged = true; _descriptionChanged = true; _positionChanged = true; @@ -4303,6 +4313,9 @@ QList EntityItemProperties::listChangedProperties() { if (userDataChanged()) { out += "userData"; } + if (privateUserDataChanged()) { + out += "privateUserData"; + } if (hrefChanged()) { out += "href"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0142f42536..af945a89de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -180,6 +180,7 @@ public: DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED); DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA); + DEFINE_PROPERTY_REF(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString, ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3); @@ -607,6 +608,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, PrivateUserData, privateUserData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner()); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index be3c566724..cc12eb37b4 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -28,6 +28,7 @@ const QVector ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC = QVector= 0) { + QString changeHint = properties.getPrivateUserData(); + changedProperties[index] = QString("privateUserData:") + changeHint; + } + } + if (properties.parentJointIndexChanged()) { int index = changedProperties.indexOf("parentJointIndex"); if (index >= 0) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 274c34a268..8c97b3ca6a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -135,6 +135,7 @@ public: AudioSoloRequest, BulkAvatarTraitsAck, StopInjector, + PrivateUserData, NUM_PACKET_TYPE }; From 0da318f2cda5e4f6407687db31618332278fef22 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 May 2019 10:37:35 -0700 Subject: [PATCH 11/83] fix material target tooltip --- scripts/system/assets/data/createAppTooltips.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json index 01f471a5cb..cc622c2184 100644 --- a/scripts/system/assets/data/createAppTooltips.json +++ b/scripts/system/assets/data/createAppTooltips.json @@ -366,17 +366,8 @@ "materialData": { "tooltip": "Can be used instead of a JSON file when material set to materialData." }, - "materialNameToReplace": { - "tooltip": "Material name of parent entity to map this material entity on.", - "jsPropertyName": "parentMaterialName" - }, - "submeshToReplace": { - "tooltip": "Submesh index of the parent entity to map this material on.", - "jsPropertyName": "parentMaterialName" - }, - "selectSubmesh": { - "tooltip": "If enabled, \"Submesh to Replace\" property will show up, otherwise \"Material to Replace\" will be shown.", - "skipJSProperty": true + "parentMaterialName": { + "tooltip": "The target mesh indices or material names that this material entity should be assigned to on it's parent. This only supports parents that are Avatars as well as Shape or Model entity types." }, "priority": { "tooltip": "The priority of the material, where a larger number means higher priority. Original materials = 0." From d0e5087b2007faee95e04e0e530df3206dc7290f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 6 May 2019 10:19:58 -0700 Subject: [PATCH 12/83] Add permission --- .../resources/describe-settings.json | 1330 +++++++++-------- domain-server/src/DomainGatekeeper.cpp | 2 + .../src/DomainServerSettingsManager.cpp | 6 + libraries/networking/src/LimitedNodeList.cpp | 5 + libraries/networking/src/LimitedNodeList.h | 2 + libraries/networking/src/Node.h | 1 + libraries/networking/src/NodePermissions.cpp | 5 + libraries/networking/src/NodePermissions.h | 3 +- 8 files changed, 713 insertions(+), 641 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 352106dcf7..fbbbec9cf2 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 2.2, + "version": 2.3, "settings": [ { "name": "metaverse", @@ -221,130 +221,138 @@ ] }, { - "name": "standard_permissions", - "type": "table", - "label": "Domain-Wide User Permissions", - "help": "Indicate which types of users can have which domain-wide permissions.", - "caption": "Standard Permissions", - "can_add_new_rows": false, - "groups": [ - { - "label": "Type of User", - "span": 1 - }, - { - "label": "Permissions ?", - "span": 10 - } - ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ], - "non-deletable-row-key": "permissions_id", - "non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ], - "default": [ - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "anonymous" - }, - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "friends" - }, - { - "id_can_adjust_locks": true, - "id_can_connect": true, - "id_can_connect_past_max_capacity": true, - "id_can_kick": true, - "id_can_replace_content": true, - "id_can_rez": true, - "id_can_rez_certified": true, - "id_can_rez_tmp": true, - "id_can_rez_tmp_certified": true, - "id_can_write_to_asset_server": true, - "permissions_id": "localhost" - }, - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "logged-in" - } - ] + "name": "standard_permissions", + "type": "table", + "label": "Domain-Wide User Permissions", + "help": "Indicate which types of users can have which domain-wide permissions.", + "caption": "Standard Permissions", + "can_add_new_rows": false, + "groups": [ + { + "label": "Type of User", + "span": 1 + }, + { + "label": "Permissions ?", + "span": 10 + } + ], + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ], + "non-deletable-row-key": "permissions_id", + "non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ], + "default": [ + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "anonymous" + }, + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "friends" + }, + { + "id_can_adjust_locks": true, + "id_can_connect": true, + "id_can_connect_past_max_capacity": true, + "id_can_kick": true, + "id_can_replace_content": true, + "id_can_rez": true, + "id_can_rez_certified": true, + "id_can_rez_tmp": true, + "id_can_rez_tmp_certified": true, + "id_can_write_to_asset_server": true, + "id_can_get_and_set_private_user_data": true, + "permissions_id": "localhost" + }, + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "logged-in" + } + ] }, { "name": "group_permissions", @@ -361,111 +369,118 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "Group Name", - "readonly": true, - "hidden": true - }, - { - "name": "rank_id", - "label": "Rank ID", - "readonly": true, - "hidden": true - }, - { - "name": "rank_order", - "label": "Rank Order", - "readonly": true, - "hidden": true - }, - { - "name": "rank_name", - "label": "", - "readonly": true - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "Group Name", + "readonly": true, + "hidden": true + }, + { + "name": "rank_id", + "label": "Rank ID", + "readonly": true, + "hidden": true + }, + { + "name": "rank_order", + "label": "Rank Order", + "readonly": true, + "hidden": true + }, + { + "name": "rank_name", + "label": "", + "readonly": true + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "group_forbiddens", @@ -482,108 +497,115 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "Group Name", - "hidden": true - }, - { - "name": "rank_id", - "label": "Rank ID", - "hidden": true - }, - { - "name": "rank_order", - "label": "Rank Order", - "hidden": true - }, - { - "name": "rank_name", - "label": "", - "readonly": true - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "Group Name", + "hidden": true + }, + { + "name": "rank_id", + "label": "Rank ID", + "hidden": true + }, + { + "name": "rank_order", + "label": "Rank Order", + "hidden": true + }, + { + "name": "rank_name", + "label": "", + "readonly": true + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "permissions", @@ -596,86 +618,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "ip_permissions", @@ -688,86 +717,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "mac_permissions", @@ -780,86 +816,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "machine_fingerprint_permissions", @@ -872,86 +915,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 10 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "multi_kick_logged_in", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 8c7beaa614..6493080987 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -282,6 +282,7 @@ void DomainGatekeeper::updateNodePermissions() { userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities; userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer; userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; + userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; } else { // at this point we don't have a sending socket for packets from this node - assume it is the active socket // or the public socket if we haven't activated a socket for the node yet @@ -374,6 +375,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities; userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer; userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; + userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; newNode->setPermissions(userPerms); return newNode; } diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 4e833f6b77..2deb51f2e3 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -441,6 +441,12 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena } } + if (oldVersion < 2.3) { + unpackPermissions(); + _standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canGetAndSetPrivateUserData); + packPermissions(); + } + // write the current description version to our settings *versionVariant = _descriptionVersion; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 82f3459c15..34eecf5ee8 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -197,6 +197,11 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { newPermissions.can(NodePermissions::Permission::canReplaceDomainContent)) { emit canReplaceContentChanged(_permissions.can(NodePermissions::Permission::canReplaceDomainContent)); } + if (originalPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData) != + newPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) { + emit canGetAndSetPrivateUserDataChanged(_permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)); + } +} } void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 8593ad4b1b..3684b50648 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -124,6 +124,7 @@ public: bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } + bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort); @@ -368,6 +369,7 @@ signals: void canWriteAssetsChanged(bool canWriteAssets); void canKickChanged(bool canKick); void canReplaceContentChanged(bool canReplaceContent); + void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData); protected slots: void connectedForLocalSocketTest(); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index fe3177d785..07c599913b 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -83,6 +83,7 @@ public: bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } + bool getCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } using NodesIgnoredPair = std::pair, bool>; diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 92ebf1d01e..e0de649c05 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -67,6 +67,7 @@ NodePermissions::NodePermissions(QMap perms) { Permission::canConnectPastMaxCapacity : Permission::none; permissions |= perms["id_can_kick"].toBool() ? Permission::canKick : Permission::none; permissions |= perms["id_can_replace_content"].toBool() ? Permission::canReplaceDomainContent : Permission::none; + permissions |= perms["id_can_get_and_set_private_user_data"].toBool() ? Permission::canGetAndSetPrivateUserData : Permission::none; } QVariant NodePermissions::toVariant(QHash groupRanks) { @@ -94,6 +95,7 @@ QVariant NodePermissions::toVariant(QHash groupRanks) { values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity); values["id_can_kick"] = can(Permission::canKick); values["id_can_replace_content"] = can(Permission::canReplaceDomainContent); + values["id_can_get_and_set_private_user_data"] = can(Permission::canGetAndSetPrivateUserData); return QVariant(values); } @@ -166,6 +168,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) { if (perms.can(NodePermissions::Permission::canReplaceDomainContent)) { debug << " can_replace_content"; } + if (perms.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) { + debug << " get-and-set-private-user-data"; + } debug.nospace() << "]"; return debug.nospace(); } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 1b0b9d220d..583c1b29ac 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -75,7 +75,8 @@ public: canKick = 64, canReplaceDomainContent = 128, canRezPermanentCertifiedEntities = 256, - canRezTemporaryCertifiedEntities = 512 + canRezTemporaryCertifiedEntities = 512, + canGetAndSetPrivateUserData = 1024 }; Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; From a68da2bca7cbfd4b5a58643884e3581ad4ba9a6e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 6 May 2019 10:47:58 -0700 Subject: [PATCH 13/83] Finish out the feature, i think --- .../entities/src/EntityScriptingInterface.cpp | 1 + .../entities/src/EntityScriptingInterface.h | 17 ++++++++++++ libraries/entities/src/EntityTree.cpp | 27 +++++++++++++++++++ libraries/networking/src/LimitedNodeList.cpp | 1 - libraries/networking/src/udt/PacketHeaders.h | 2 +- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f8c45b792a..daeb4a8f1f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -56,6 +56,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged); connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged); connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); + connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 38b73aaf45..c072dedaf9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -236,6 +236,14 @@ public slots: */ Q_INVOKABLE bool canReplaceContent(); + /**jsdoc + * Check whether or not you can get and set private user data. + * @function Entities.canGetAndSetPrivateUserData + * @returns {boolean} true if the domain server will allow the user to get and set private user data, + * otherwise false. + */ + Q_INVOKABLE bool canGetAndSetPrivateUserData(); + /**jsdoc *

How an entity is sent over the wire.

* @@ -1861,6 +1869,15 @@ signals: */ void canWriteAssetsChanged(bool canWriteAssets); + /**jsdoc + * Triggered when your ability to get and set private user data changes. + * @function Entities.canGetAndSetPrivateUserDataChanged + * @param {boolean} canGetAndSetPrivateUserData - true if you can change the privateUserData property of an entity, + * otherwise false. + * @returns {Signal} + */ + void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData); + /**jsdoc * Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 806f6189e2..260d044693 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1780,6 +1780,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c bool suppressDisallowedClientScript = false; bool suppressDisallowedServerScript = false; + bool suppressDisallowedPrivateUserData = false; bool isPhysics = message.getType() == PacketType::EntityPhysics; _totalEditMessages++; @@ -1869,6 +1870,27 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } + if (!properties.getPrivateUserData().isEmpty()) { + if (!senderNode->getCanGetAndSetPrivateUserData()) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] is attempting to set private user data but user isn't allowed; edit rejected..."; + } + + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + // Make sure we didn't already need to send back a delete because the client script failed + // the whitelist check + if (!wasDeletedBecauseOfClientScript) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + } + } else { + suppressDisallowedPrivateUserData = true; + } + } + } } if (!isClone) { @@ -1923,6 +1945,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c properties.setServerScripts(existingEntity->getServerScripts()); } + if (suppressDisallowedPrivateUserData) { + bumpTimestamp(properties); + properties.setPrivateUserData(existingEntity->getPrivateUserData()); + } + // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 34eecf5ee8..c2f606e7dd 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -202,7 +202,6 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { emit canGetAndSetPrivateUserDataChanged(_permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)); } } -} void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) { if (QThread::currentThread() != thread()) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 8c97b3ca6a..5de7e56e47 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -135,7 +135,6 @@ public: AudioSoloRequest, BulkAvatarTraitsAck, StopInjector, - PrivateUserData, NUM_PACKET_TYPE }; @@ -270,6 +269,7 @@ enum class EntityVersion : PacketVersion { CertificateTypeProperty, DisableWebMedia, ParticleShapeType, + PrivateUserData, // Add new versions above here NUM_PACKET_TYPE, From 1c9a4fd3229dae18f04da5ae35c2b7298a292309 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 6 May 2019 11:37:15 -0700 Subject: [PATCH 14/83] Fix build error --- libraries/entities/src/EntityScriptingInterface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index daeb4a8f1f..87f15896f2 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -108,6 +108,11 @@ bool EntityScriptingInterface::canReplaceContent() { return nodeList->getThisNodeCanReplaceContent(); } +bool EntityScriptingInterface::canGetAndSetPrivateUserData() { + auto nodeList = DependencyManager::get(); + return nodeList->getThisNodeCanGetAndSetPrivateUserData(); +} + void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntityPointer, this, &EntityScriptingInterface::onAddingEntity); From 6033058d54cbd6be5e9ef1d690c822ee95fc62af Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 6 May 2019 11:51:59 -0700 Subject: [PATCH 15/83] Comment --- libraries/entities/src/EntityItemProperties.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 86f293f2ea..da96077bc3 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -819,7 +819,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * which you can manipulate the properties of, and use JSON.stringify() to convert the object into a string to * put in the property. * - * @property {string} privateUserData="" - Like userData, but only accessible by the Entity Server Scripts. + * @property {string} privateUserData="" - Like userData, but only accessible by Entity Server Scripts, AC scripts, and users + * who are given "Can Get and Set Private User Data" permissions from the ACL matrix on the Domain Settings page. * * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. * @property {number} scriptTimestamp=0 - Intended to be used to indicate when the client entity script was loaded. Should be From d53b5289942dbf374b30898cbb81e6ac3e75da25 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 6 May 2019 13:38:09 -0700 Subject: [PATCH 16/83] Some fixes - i don't think this code is complete --- .../resources/describe-settings.json | 14 ++++---- .../entities/src/EntityEditPacketSender.cpp | 4 +++ libraries/entities/src/EntityItem.cpp | 7 ++-- libraries/entities/src/EntityTree.cpp | 32 ++++++++----------- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 55a6d3f645..d3a550b801 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -234,7 +234,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -370,7 +370,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -498,7 +498,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -619,7 +619,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -718,7 +718,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -817,7 +817,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ @@ -916,7 +916,7 @@ }, { "label": "Permissions ?", - "span": 10 + "span": 11 } ], "columns": [ diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 0491bdedae..dbb3ab076e 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -110,6 +110,10 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityPropertyFlags requestedProperties = propertiesCopy.getChangedProperties(); + if (!nodeList->getThisNodeCanGetAndSetPrivateUserData() && requestedProperties.getHasProperty(PROP_PRIVATE_USER_DATA)) { + requestedProperties -= PROP_PRIVATE_USER_DATA; + } + while (encodeResult == OctreeElement::PARTIAL) { encodeResult = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut, requestedProperties, didntFitProperties); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index dc871e655f..148ea35133 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -86,6 +86,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_NAME; requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; + requestedProperties += PROP_PRIVATE_USER_DATA; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; requestedProperties += PROP_POSITION; @@ -266,8 +267,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray()); // convert AVATAR_SELF_ID to actual sessionUUID. QUuid actualParentID = getParentID(); + auto nodeList = DependencyManager::get(); if (actualParentID == AVATAR_SELF_ID) { - auto nodeList = DependencyManager::get(); actualParentID = nodeList->getSessionUUID(); } APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, actualParentID); @@ -276,7 +277,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); - APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, getPrivateUserData()); + if (nodeList->getThisNodeCanGetAndSetPrivateUserData()) { + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, getPrivateUserData()); + } APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 260d044693..cc4f526646 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1869,26 +1869,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } } + } - if (!properties.getPrivateUserData().isEmpty()) { - if (!senderNode->getCanGetAndSetPrivateUserData()) { - if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() - << "] is attempting to set private user data but user isn't allowed; edit rejected..."; - } + if (!properties.getPrivateUserData().isEmpty() && validEditPacket) { + if (!senderNode->getCanGetAndSetPrivateUserData()) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] is attempting to set private user data but user isn't allowed; edit rejected..."; + } - // If this was an add, we also want to tell the client that sent this edit that the entity was not added. - if (isAdd) { - // Make sure we didn't already need to send back a delete because the client script failed - // the whitelist check - if (!wasDeletedBecauseOfClientScript) { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - validEditPacket = false; - } - } else { - suppressDisallowedPrivateUserData = true; - } + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + } else { + suppressDisallowedPrivateUserData = true; } } } From 7fb7e503f9e66a426056bc7657f5e1163efdacd0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:43:49 -0700 Subject: [PATCH 17/83] reduce footprint of ShapeManager::_shapeMap --- libraries/shared/src/HashKey.cpp | 6 +++--- libraries/shared/src/HashKey.h | 19 ++++++++++++------- libraries/shared/src/ShapeInfo.cpp | 24 ++++++++++++------------ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/libraries/shared/src/HashKey.cpp b/libraries/shared/src/HashKey.cpp index 488eccb1bf..8c5303028b 100644 --- a/libraries/shared/src/HashKey.cpp +++ b/libraries/shared/src/HashKey.cpp @@ -51,15 +51,15 @@ float HashKey::getNumQuantizedValuesPerMeter() { return QUANTIZED_VALUES_PER_METER; } -void HashKey::hashUint64(uint64_t data) { +void HashKey::Hasher::hashUint64(uint64_t data) { _hash += squirrel3_64(data, ++_hashCount); } -void HashKey::hashFloat(float data) { +void HashKey::Hasher::hashFloat(float data) { _hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount); } -void HashKey::hashVec3(const glm::vec3& data) { +void HashKey::Hasher::hashVec3(const glm::vec3& data) { _hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); _hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); _hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h index 446eb4c25f..071b162a50 100644 --- a/libraries/shared/src/HashKey.h +++ b/libraries/shared/src/HashKey.h @@ -30,6 +30,18 @@ class HashKey { public: + class Hasher { + public: + Hasher() {} + void hashUint64(uint64_t data); + void hashFloat(float data); + void hashVec3(const glm::vec3& data); + uint64_t getHash64() const { return _hash; } + private: + uint64_t _hash { 0 }; + uint8_t _hashCount { 0 }; + }; + static float getNumQuantizedValuesPerMeter(); HashKey() {} @@ -39,16 +51,9 @@ public: bool equals(const HashKey& other) const { return _hash == other._hash; } int32_t getHash() const { return (int32_t)((uint32_t)_hash); } - // These methods for accumulating a hash. - void hashUint64(uint64_t data); - void hashFloat(float data); - void hashVec3(const glm::vec3& data); - uint64_t getHash64() const { return _hash; } - private: uint64_t _hash { 0 }; - uint8_t _hashCount { 0 }; }; #endif // hifi_HashKey_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index bf51e455c5..1b9054c4d6 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -273,18 +273,18 @@ float ShapeInfo::computeVolume() const { uint64_t ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) { - HashKey hashKey; + HashKey::Hasher hasher; // The key is not yet cached therefore we must compute it. - hashKey.hashUint64((uint64_t)_type); + hasher.hashUint64((uint64_t)_type); if (_type == SHAPE_TYPE_MULTISPHERE) { for (auto &sphereData : _sphereCollection) { - hashKey.hashVec3(glm::vec3(sphereData)); - hashKey.hashFloat(sphereData.w); + hasher.hashVec3(glm::vec3(sphereData)); + hasher.hashFloat(sphereData.w); } } else if (_type != SHAPE_TYPE_SIMPLE_HULL) { - hashKey.hashVec3(_halfExtents); - hashKey.hashVec3(_offset); + hasher.hashVec3(_halfExtents); + hasher.hashVec3(_offset); } else { // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique // descriptive string. Shapes that are uniquely described by their type and URL could just put their @@ -294,7 +294,7 @@ uint64_t ShapeInfo::getHash() const { const int numPoints = (int)points.size(); for (int i = 0; i < numPoints; ++i) { - hashKey.hashVec3(points[i]); + hasher.hashVec3(points[i]); } } @@ -302,19 +302,19 @@ uint64_t ShapeInfo::getHash() const { if (!url.isEmpty()) { QByteArray baUrl = url.toLocal8Bit(); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); - hashKey.hashUint64((uint64_t)urlHash); + hasher.hashUint64((uint64_t)urlHash); } if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { uint64_t numHulls = (uint64_t)_pointCollection.size(); - hashKey.hashUint64(numHulls); + hasher.hashUint64(numHulls); } else if (_type == SHAPE_TYPE_MULTISPHERE) { uint64_t numSpheres = (uint64_t)_sphereCollection.size(); - hashKey.hashUint64(numSpheres); + hasher.hashUint64(numSpheres); } else if (_type == SHAPE_TYPE_SIMPLE_HULL) { - hashKey.hashUint64(1); + hasher.hashUint64(1); } - _hash64 = hashKey.getHash64(); + _hash64 = hasher.getHash64(); } return _hash64; } From fbd4db55057caf622b1db101c354a4bc272d5932 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:48:24 -0700 Subject: [PATCH 18/83] build StaticMeshShapes outside of mainloop --- libraries/physics/src/ShapeFactory.cpp | 5 ++ libraries/physics/src/ShapeFactory.h | 13 +++++ libraries/physics/src/ShapeManager.cpp | 77 ++++++++++++++++++++++---- libraries/physics/src/ShapeManager.h | 10 +++- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5808a539d6..d988e88bbe 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -481,3 +481,8 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) { } delete nonConstShape; } + +void ShapeFactory::Worker::run() { + shape = ShapeFactory::createShapeFromInfo(shapeInfo); + emit submitWork(this); +} diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 704a7804b3..d23785d513 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include @@ -22,6 +24,17 @@ namespace ShapeFactory { const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); + + class Worker : public QObject, public QRunnable { + Q_OBJECT + public: + Worker(const ShapeInfo& info) : shapeInfo(info), shape(nullptr) {} + void run() override; + ShapeInfo shapeInfo; + const btCollisionShape* shape; + signals: + void submitWork(Worker*); + }; }; #endif // hifi_ShapeFactory_h diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 8acbe51540..00af50813e 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -12,10 +12,8 @@ #include "ShapeManager.h" #include +#include -#include - -#include "ShapeFactory.h" const int MAX_RING_SIZE = 256; @@ -42,13 +40,36 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { shapeRef->refCount++; return shapeRef->shape; } - const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); - if (shape) { - ShapeReference newRef; - newRef.refCount = 1; - newRef.shape = shape; - newRef.key = info.getHash(); - _shapeMap.insert(hashKey, newRef); + const btCollisionShape* shape = nullptr; + if (info.getType() == SHAPE_TYPE_STATIC_MESH) { + uint64_t hash = info.getHash(); + const auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), hash); + if (itr == _pendingMeshShapes.end()) { + // start a worker + _pendingMeshShapes.push_back(hash); + // try to recycle old deadWorker + ShapeFactory::Worker* worker = _deadWorker; + if (!worker) { + worker = new ShapeFactory::Worker(info); + } else { + worker->shapeInfo = info; + _deadWorker = nullptr; + } + // we will delete worker manually later + worker->setAutoDelete(false); + QObject::connect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); + QThreadPool::globalInstance()->start(worker); + } + // else we're still waiting for the shape to be created on another thread + } else { + shape = ShapeFactory::createShapeFromInfo(info); + if (shape) { + ShapeReference newRef; + newRef.refCount = 1; + newRef.shape = shape; + newRef.key = info.getHash(); + _shapeMap.insert(hashKey, newRef); + } } return shape; } @@ -153,3 +174,39 @@ bool ShapeManager::hasShape(const btCollisionShape* shape) const { } return false; } + +// slot: called when ShapeFactory::Worker is done building shape +void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { + auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), worker->shapeInfo.getHash()); + if (itr == _pendingMeshShapes.end()) { + // we've received a shape but don't remember asking for it + // (should not fall in here, but if we do: delete the unwanted shape) + if (worker->shape) { + ShapeFactory::deleteShape(worker->shape); + } + } else { + // clear pending status + *itr = _pendingMeshShapes.back(); + _pendingMeshShapes.pop_back(); + + // cache the new shape + if (worker->shape) { + ShapeReference newRef; + newRef.refCount = 1; + newRef.shape = worker->shape; + newRef.key = worker->shapeInfo.getHash(); + HashKey hashKey(newRef.key); + _shapeMap.insert(hashKey, newRef); + } + } + disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); + + if (_deadWorker) { + // delete the previous deadWorker manually + delete _deadWorker; + } + // save this dead worker for later + worker->shapeInfo.clear(); + worker->shape = nullptr; + _deadWorker = worker; +} diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index c1fb57e017..898d1b0f37 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -14,11 +14,13 @@ #include +#include #include #include #include +#include "ShapeFactory.h" #include "HashKey.h" // The ShapeManager handles the ref-counting on shared shapes: @@ -44,7 +46,8 @@ // entries that still have zero ref-count. -class ShapeManager { +class ShapeManager : public QObject { + Q_OBJECT public: ShapeManager(); @@ -65,6 +68,9 @@ public: int getNumReferences(const btCollisionShape* shape) const; bool hasShape(const btCollisionShape* shape) const; +protected slots: + void acceptWork(ShapeFactory::Worker* worker); + private: bool releaseShapeByKey(uint64_t key); @@ -79,6 +85,8 @@ private: // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; std::vector _garbageRing; + std::vector _pendingMeshShapes; + ShapeFactory::Worker* _deadWorker { nullptr }; uint32_t _ringIndex { 0 }; }; From 514d598797a4dda0a2d4de4d49e665ff9e3357e7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:53:08 -0700 Subject: [PATCH 19/83] remove unhelpful profiling with wrong category --- libraries/entities/src/EntityTree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b3dd78ae92..688f10cdee 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2079,7 +2079,6 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } void EntityTree::fixupNeedsParentFixups() { - PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; QVector entitiesToFixup; { From 8445eaf310706030733837a5ea984aa01025125a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:54:29 -0700 Subject: [PATCH 20/83] protect against orphaned off-thread-assembled shapes --- libraries/physics/src/ShapeManager.cpp | 90 +++++++++++++++++++------- libraries/physics/src/ShapeManager.h | 11 ++++ 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 00af50813e..1de92f67a4 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -14,11 +14,13 @@ #include #include +#include const int MAX_RING_SIZE = 256; ShapeManager::ShapeManager() { _garbageRing.reserve(MAX_RING_SIZE); + _nextOrphanExpiry = std::chrono::steady_clock::now(); } ShapeManager::~ShapeManager() { @@ -74,6 +76,33 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { return shape; } +void ShapeManager::addToGarbage(uint64_t key) { + // look for existing entry in _garbageRing + int32_t ringSize = (int32_t)(_garbageRing.size()); + for (int32_t i = 0; i < ringSize; ++i) { + int32_t j = (_ringIndex + ringSize) % ringSize; + if (_garbageRing[j] == key) { + // already on the list, don't add it again + return; + } + } + if (ringSize == MAX_RING_SIZE) { + // remove one + HashKey hashKeyToRemove(_garbageRing[_ringIndex]); + ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); + if (shapeRef && shapeRef->refCount == 0) { + ShapeFactory::deleteShape(shapeRef->shape); + _shapeMap.remove(hashKeyToRemove); + } + // replace at _ringIndex and advance + _garbageRing[_ringIndex] = key; + _ringIndex = (_ringIndex + 1) % ringSize; + } else { + // add one + _garbageRing.push_back(key); + } +} + // private helper method bool ShapeManager::releaseShapeByKey(uint64_t key) { HashKey hashKey(key); @@ -82,30 +111,7 @@ bool ShapeManager::releaseShapeByKey(uint64_t key) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { - // look for existing entry in _garbageRing - int32_t ringSize = (int32_t)(_garbageRing.size()); - for (int32_t i = 0; i < ringSize; ++i) { - int32_t j = (_ringIndex + ringSize) % ringSize; - if (_garbageRing[j] == key) { - // already on the list, don't add it again - return true; - } - } - if (ringSize == MAX_RING_SIZE) { - // remove one - HashKey hashKeyToRemove(_garbageRing[_ringIndex]); - ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); - if (shapeRef && shapeRef->refCount == 0) { - ShapeFactory::deleteShape(shapeRef->shape); - _shapeMap.remove(hashKeyToRemove); - } - // replace at _ringIndex and advance - _garbageRing[_ringIndex] = key; - _ringIndex = (_ringIndex + 1) % ringSize; - } else { - // add one - _garbageRing.push_back(key); - } + addToGarbage(key); } return true; } else { @@ -192,11 +198,45 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { // cache the new shape if (worker->shape) { ShapeReference newRef; - newRef.refCount = 1; + // refCount is zero because nothing is using the shape yet + newRef.refCount = 0; newRef.shape = worker->shape; newRef.key = worker->shapeInfo.getHash(); HashKey hashKey(newRef.key); _shapeMap.insert(hashKey, newRef); + + // This shape's refCount is zero because an object requested it but is not yet using it. We expect it to be + // used later but there is a possibility it will never be used (e.g. the object that wanted it was removed + // before the shape could be added, or has changed its mind and now wants a different shape). + // Normally zero refCount shapes belong on _garbageRing for possible cleanup but we don't want to add it there + // because it might get reaped too soon. So we add it to _orphans to check later. If it still has zero + // refCount on expiry we will move it to _garbageRing. + const int64_t SHAPE_EXPIRY = USECS_PER_SECOND; + auto now = std::chrono::steady_clock::now(); + auto expiry = now + std::chrono::microseconds(SHAPE_EXPIRY); + if (_nextOrphanExpiry < now) { + // check for expired orphan shapes + size_t i = 0; + while (i < _orphans.size()) { + if (_orphans[i].expiry < now) { + uint64_t key = _orphans[i].key; + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); + if (shapeRef) { + if (shapeRef->refCount == 0) { + // shape unused after expiry + addToGarbage(key); + } + } + _orphans[i] = _orphans.back(); + _orphans.pop_back(); + } else { + ++i; + } + } + } + _nextOrphanExpiry = expiry; + _orphans.push_back(KeyExpiry(newRef.key, expiry)); } } disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 898d1b0f37..f3423a5ac6 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -72,6 +72,7 @@ protected slots: void acceptWork(ShapeFactory::Worker* worker); private: + void addToGarbage(uint64_t key); bool releaseShapeByKey(uint64_t key); class ShapeReference { @@ -82,11 +83,21 @@ private: ShapeReference() : refCount(0), shape(nullptr) {} }; + using TimePoint = std::chrono::time_point; + class KeyExpiry { + public: + KeyExpiry(uint64_t k, std::chrono::time_point e) : expiry(e), key(k) {} + TimePoint expiry; + uint64_t key; + }; + // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; std::vector _garbageRing; std::vector _pendingMeshShapes; + std::vector _orphans; ShapeFactory::Worker* _deadWorker { nullptr }; + TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; }; From 7dfa8a26a3ec7c3200a67d4986acb64dda017b60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:58:38 -0700 Subject: [PATCH 21/83] cleanup EntityItem::shouldBePhysical() and derivations --- .../src/RenderableModelEntityItem.cpp | 14 ++++++++------ .../src/RenderablePolyVoxEntityItem.h | 1 - libraries/entities/src/EntityItem.h | 2 +- libraries/entities/src/ModelEntityItem.cpp | 5 ----- libraries/entities/src/ModelEntityItem.h | 2 -- libraries/entities/src/PolyVoxEntityItem.h | 1 - libraries/entities/src/ShapeEntityItem.h | 2 -- 7 files changed, 9 insertions(+), 18 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bfbbe12ea6..7602028e36 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -735,13 +735,15 @@ bool RenderableModelEntityItem::shouldBePhysical() const { auto model = getModel(); // If we have a model, make sure it hasn't failed to download. // If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready. - if (model && (getShapeType() == SHAPE_TYPE_COMPOUND || getShapeType() == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) { - return false; - } else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { - return false; - } else { - return ModelEntityItem::shouldBePhysical(); + ShapeType shapeType = getShapeType(); + if (model) { + if ((shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) { + return false; + } else if (shapeType != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { + return false; + } } + return !isDead() && shapeType != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid(); } int RenderableModelEntityItem::getJointParent(int index) const { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 7aea87535e..cca34767a4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -72,7 +72,6 @@ public: glm::mat4 localToVoxelMatrix() const; virtual ShapeType getShapeType() const override; - virtual bool shouldBePhysical() const override { return !isDead(); } virtual bool isReadyToComputeShape() const override; virtual void computeShapeInfo(ShapeInfo& info) override; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 29a1a8d73c..dc3c467ca1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -322,7 +322,7 @@ public: bool getDynamic() const; void setDynamic(bool value); - virtual bool shouldBePhysical() const { return false; } + virtual bool shouldBePhysical() const { return !isDead(); } bool isVisuallyReady() const { return _visuallyReady; } bool getLocked() const; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 505ee26c0f..cb9637acd5 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -421,11 +421,6 @@ void ModelEntityItem::setAnimationFPS(float value) { }); } -// virtual -bool ModelEntityItem::shouldBePhysical() const { - return !isDead() && getShapeType() != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid(); -} - void ModelEntityItem::resizeJointArrays(int newSize) { if (newSize < 0) { return; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index d532fefe7e..75a695f1c0 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -118,8 +118,6 @@ public: const QString getTextures() const; void setTextures(const QString& textures); - virtual bool shouldBePhysical() const override; - virtual void setJointRotations(const QVector& rotations); virtual void setJointRotationsSet(const QVector& rotationsSet); virtual void setJointTranslations(const QVector& translations); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index dd05ce67b1..a6076dfda7 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -164,7 +164,6 @@ class PolyVoxEntityItem : public EntityItem { glm::vec3 getSurfacePositionAdjustment() const; virtual ShapeType getShapeType() const override; - virtual bool shouldBePhysical() const override { return !isDead(); } bool isEdged() const; diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index fc590e06a4..3622c74f50 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,8 +84,6 @@ public: void setUnscaledDimensions(const glm::vec3& value) override; - bool shouldBePhysical() const override { return !isDead(); } - bool supportsDetailedIntersection() const override; bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, From deee1598923eca51e78d9ca3613e2ae8f06b095c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Apr 2019 08:37:39 -0700 Subject: [PATCH 22/83] unravel physics-vs-entities lib dependencies part 1 --- libraries/entities/src/EntityItem.h | 3 ++- libraries/entities/src/SimulationOwner.h | 3 +++ libraries/physics/src/EntityMotionState.h | 1 + libraries/physics/src/ObjectActionTractor.cpp | 3 ++- libraries/physics/src/ObjectActionTravelOriented.cpp | 4 +++- libraries/physics/src/ObjectDynamic.h | 2 ++ libraries/physics/src/ObjectMotionState.h | 2 +- libraries/{entities => shared}/src/SimulationFlags.h | 11 +++++++++++ 8 files changed, 25 insertions(+), 4 deletions(-) rename libraries/{entities => shared}/src/SimulationFlags.h (79%) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index dc3c467ca1..303b12cffd 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -23,6 +23,7 @@ #include // for OctreeElement::AppendState #include #include +#include #include #include #include @@ -33,11 +34,11 @@ #include "EntityPropertyFlags.h" #include "EntityTypes.h" #include "SimulationOwner.h" -#include "SimulationFlags.h" #include "EntityDynamicInterface.h" #include "GrabPropertyGroup.h" class EntitySimulation; +using EntitySimulationPointer = std::shared_ptr; class EntityTreeElement; class EntityTreeElementExtraEncodeData; class EntityDynamicInterface; diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index f574234354..6f37066e47 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -89,6 +89,8 @@ // (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may // bid for it immediately at VOLUNTEER. // +/* These declarations temporarily moved to SimulationFlags.h while we unravel some spaghetti dependencies. + * The intent is to move them back here once the dust settles. const uint8_t YIELD_SIMULATION_PRIORITY = 1; const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1; const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; @@ -101,6 +103,7 @@ const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY // which really just means: things that collide with it will be bid at a priority level one lower const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY; const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY; +*/ class SimulationOwner { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ddf384dc77..6e1ea66685 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -12,6 +12,7 @@ #ifndef hifi_EntityMotionState_h #define hifi_EntityMotionState_h +#include #include #include #include diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index c7681e217c..b53c2e137a 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -11,7 +11,8 @@ #include "ObjectActionTractor.h" -#include "QVariantGLM.h" +#include +#include #include "PhysicsLogging.h" diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp index bd32ed13ff..b27ec40ae4 100644 --- a/libraries/physics/src/ObjectActionTravelOriented.cpp +++ b/libraries/physics/src/ObjectActionTravelOriented.cpp @@ -13,7 +13,9 @@ #include -#include "QVariantGLM.h" +#include +#include + #include "PhysicsLogging.h" const uint16_t ObjectActionTravelOriented::actionVersion = 1; diff --git a/libraries/physics/src/ObjectDynamic.h b/libraries/physics/src/ObjectDynamic.h index f41d6cd51d..49fa615b88 100644 --- a/libraries/physics/src/ObjectDynamic.h +++ b/libraries/physics/src/ObjectDynamic.h @@ -17,7 +17,9 @@ #include +#include #include +#include #include "ObjectMotionState.h" #include "BulletUtil.h" diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index fe175a2c7d..a53215753e 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include "ContactInfo.h" #include "ShapeManager.h" diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/shared/src/SimulationFlags.h similarity index 79% rename from libraries/entities/src/SimulationFlags.h rename to libraries/shared/src/SimulationFlags.h index c45b333b29..af8c0e5fce 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/shared/src/SimulationFlags.h @@ -12,6 +12,17 @@ #ifndef hifi_SimulationFlags_h #define hifi_SimulationFlags_h +const uint8_t YIELD_SIMULATION_PRIORITY = 1; +const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1; +const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; + +const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128; +const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1; + +const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY; +const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY; + + namespace Simulation { const uint32_t DIRTY_POSITION = 0x0001; const uint32_t DIRTY_ROTATION = 0x0002; From 3c8c68d18787f6f40a34e0ccf4ab5296cb7aaf21 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Apr 2019 13:51:55 -0700 Subject: [PATCH 23/83] track EntityItem adds to physcis with pending shape --- libraries/entities/src/EntityItem.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 122 +++++++++++++----- .../physics/src/PhysicalEntitySimulation.h | 14 ++ libraries/physics/src/ShapeFactory.cpp | 2 + libraries/physics/src/ShapeManager.cpp | 18 +++ libraries/physics/src/ShapeManager.h | 7 + 6 files changed, 133 insertions(+), 32 deletions(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 303b12cffd..b99536f4dc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -323,7 +323,7 @@ public: bool getDynamic() const; void setDynamic(bool value); - virtual bool shouldBePhysical() const { return !isDead(); } + virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE; } bool isVisuallyReady() const { return _visuallyReady; } bool getLocked() const; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 8e5248f6a9..515a99929c 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -230,19 +230,16 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() { QMutexLocker lock(&_mutex); for (auto entity: _entitiesToRemoveFromPhysics) { - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - assert(motionState); - // TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen - if (motionState) { - _entitiesToAddToPhysics.remove(entity); - if (entity->isDead() && entity->getElement()) { - _deadEntities.insert(entity); - } + _entitiesToAddToPhysics.remove(entity); + if (entity->isDead() && entity->getElement()) { + _deadEntities.insert(entity); + } + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { _incomingChanges.remove(motionState); removeOwnershipData(motionState); _physicalObjects.remove(motionState); - // remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine) _objectsToDelete.push_back(motionState); } @@ -264,7 +261,75 @@ void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() { void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) { result.clear(); + + // this lambda for when we decide to actually build the motionState + auto buildMotionState = [&](btCollisionShape* shape, EntityItemPointer entity) { + EntityMotionState* motionState = new EntityMotionState(shape, entity); + entity->setPhysicsInfo(static_cast(motionState)); + motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); + _physicalObjects.insert(motionState); + result.push_back(motionState); + }; + + // TODO: + // (1) make all changes to MotionState in "managers" (PhysicalEntitySimulation and AvatarManager) + // (2) store relevant change-flags on MotionState (maybe just EASY or HARD?) + // (3) remove knowledge of PhysicsEngine from ObjectMotionState + // (4) instead PhysicsEngine gets list of changed MotionStates, reads change-flags and applies changes accordingly + QMutexLocker lock(&_mutex); + uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); + if (deliveryCount != _lastWorkDeliveryCount) { + // new off-thread shapes have arrived --> find adds whose shapes have arrived + _lastWorkDeliveryCount = deliveryCount; + ShapeRequests::iterator requestItr = _shapeRequests.begin(); + while (requestItr != _shapeRequests.end()) { + EntityItemPointer entity = requestItr->entity; + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (!motionState) { + // this is an ADD because motionState doesn't exist yet + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash)); + if (shape) { + // shape is ready at last! + // But the entity's desired shape might have changed since last requested + // --> rebuild the ShapeInfo to verify hash + // TODO? is there a better way to do this? + ShapeInfo shapeInfo; + entity->computeShapeInfo(shapeInfo); + if (shapeInfo.getHash() != requestItr->shapeHash) { + // bummer, the hashes are different and we no longer want the shape we've received + ObjectMotionState::getShapeManager()->releaseShape(shape); + // try again + shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + buildMotionState(shape, entity); + requestItr = _shapeRequests.erase(requestItr); + } else { + requestItr->shapeHash = shapeInfo.getHash(); + ++requestItr; + } + } else { + buildMotionState(shape, entity); + requestItr = _shapeRequests.erase(requestItr); + } + } else { + // shape not ready + ++requestItr; + } + } else { + // this is a CHANGE because motionState already exists + if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) { + // TODO? reset DIRTY_SHAPE flag? + _incomingChanges.insert(motionState); + requestItr = _shapeRequests.erase(requestItr); + } else { + // shape not ready + ++requestItr; + } + } + } + } + SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin(); while (entityItr != _entitiesToAddToPhysics.end()) { EntityItemPointer entity = (*entityItr); @@ -282,30 +347,25 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } } } else if (entity->isReadyToComputeShape()) { - ShapeInfo shapeInfo; - entity->computeShapeInfo(shapeInfo); - int numPoints = shapeInfo.getLargestSubshapePointCount(); - if (shapeInfo.getType() == SHAPE_TYPE_COMPOUND) { - if (numPoints > MAX_HULL_POINTS) { - qWarning() << "convex hull with" << numPoints - << "points for entity" << entity->getName() - << "at" << entity->getWorldPosition() << " will be reduced"; + // check to see if we're waiting for a shape + ShapeRequest shapeRequest(entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + ShapeInfo shapeInfo; + entity->computeShapeInfo(shapeInfo); + uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + buildMotionState(shape, entity); + } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { + // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait + shapeRequest.shapeHash = shapeInfo.getHash(); + _shapeRequests.insert(shapeRequest); + } else { + // failed to build shape --> will not be added } } - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - EntityMotionState* motionState = new EntityMotionState(shape, entity); - entity->setPhysicsInfo(static_cast(motionState)); - _physicalObjects.insert(motionState); - result.push_back(motionState); - entityItr = _entitiesToAddToPhysics.erase(entityItr); - - // make sure the motionState's region is up-to-date before it is actually added to physics - motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); - } else { - //qWarning() << "Failed to generate new shape for entity." << entity->getName(); - ++entityItr; - } + entityItr = _entitiesToAddToPhysics.erase(entityItr); } else { ++entityItr; } diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 843069e247..65a2b8f90d 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -13,6 +13,7 @@ #define hifi_PhysicalEntitySimulation_h #include +#include #include #include @@ -98,6 +99,15 @@ public: void sendOwnedUpdates(uint32_t numSubsteps); private: + class ShapeRequest { + public: + ShapeRequest() : entity(), shapeHash(0) {} + ShapeRequest(const EntityItemPointer& e) : entity(e), shapeHash(0) {} + bool operator<(const ShapeRequest& other) const { return entity.get() < other.entity.get(); } + bool operator==(const ShapeRequest& other) const { return entity.get() == other.entity.get(); } + EntityItemPointer entity; + mutable uint64_t shapeHash; + }; SetOfEntities _entitiesToAddToPhysics; SetOfEntities _entitiesToRemoveFromPhysics; @@ -108,6 +118,9 @@ private: SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine + using ShapeRequests = std::set; + ShapeRequests _shapeRequests; + PhysicsEnginePointer _physicsEngine = nullptr; EntityEditPacketSender* _entityPacketSender = nullptr; @@ -117,6 +130,7 @@ private: workload::SpacePointer _space; uint64_t _nextBidExpiry; uint32_t _lastStepSendPackets { 0 }; + uint32_t _lastWorkDeliveryCount { 0 }; }; diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index d988e88bbe..9e0103e859 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -433,6 +433,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) } } break; + default: + break; } if (shape) { if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) { diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 1de92f67a4..85587a6c67 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -49,6 +49,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (itr == _pendingMeshShapes.end()) { // start a worker _pendingMeshShapes.push_back(hash); + ++_workRequestCount; // try to recycle old deadWorker ShapeFactory::Worker* worker = _deadWorker; if (!worker) { @@ -76,6 +77,22 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { return shape; } +const btCollisionShape* ShapeManager::getShapeByKey(uint64_t key) { + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); + if (shapeRef) { + shapeRef->refCount++; + return shapeRef->shape; + } + return nullptr; +} + +bool ShapeManager::hasShapeWithKey(uint64_t key) const { + HashKey hashKey(key); + const ShapeReference* shapeRef = _shapeMap.find(hashKey); + return (bool)shapeRef; +} + void ShapeManager::addToGarbage(uint64_t key) { // look for existing entry in _garbageRing int32_t ringSize = (int32_t)(_garbageRing.size()); @@ -249,4 +266,5 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { worker->shapeInfo.clear(); worker->shape = nullptr; _deadWorker = worker; + ++_workDeliveryCount; } diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index f3423a5ac6..b07999ec64 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -12,6 +12,7 @@ #ifndef hifi_ShapeManager_h #define hifi_ShapeManager_h +#include #include #include @@ -55,6 +56,8 @@ public: /// \return pointer to shape const btCollisionShape* getShape(const ShapeInfo& info); + const btCollisionShape* getShapeByKey(uint64_t key); + bool hasShapeWithKey(uint64_t key) const; /// \return true if shape was found and released bool releaseShape(const btCollisionShape* shape); @@ -67,6 +70,8 @@ public: int getNumReferences(const ShapeInfo& info) const; int getNumReferences(const btCollisionShape* shape) const; bool hasShape(const btCollisionShape* shape) const; + uint32_t getWorkRequestCount() const { return _workRequestCount; } + uint32_t getWorkDeliveryCount() const { return _workDeliveryCount; } protected slots: void acceptWork(ShapeFactory::Worker* worker); @@ -99,6 +104,8 @@ private: ShapeFactory::Worker* _deadWorker { nullptr }; TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; + std::atomic_uint32_t _workRequestCount { 0 }; + std::atomic_uint32_t _workDeliveryCount { 0 }; }; #endif // hifi_ShapeManager_h From 3eed8218ca556b37f25e34b1d7c3105395898a8a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 May 2019 11:41:40 -0700 Subject: [PATCH 24/83] overhaul of MotionState and shape creations --- interface/src/Application.cpp | 44 +----- interface/src/avatar/AvatarManager.cpp | 137 ++++++++++++------ interface/src/avatar/AvatarManager.h | 4 +- interface/src/avatar/AvatarMotionState.cpp | 13 +- interface/src/avatar/AvatarMotionState.h | 49 +++---- interface/src/avatar/DetailedMotionState.cpp | 27 +--- interface/src/avatar/DetailedMotionState.h | 59 ++++---- .../src/avatar/MyCharacterController.cpp | 26 +--- interface/src/avatar/MyCharacterController.h | 10 +- interface/src/avatar/OtherAvatar.cpp | 68 ++------- interface/src/avatar/OtherAvatar.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 55 +------ libraries/physics/src/EntityMotionState.h | 12 +- libraries/physics/src/ObjectMotionState.cpp | 47 +----- libraries/physics/src/ObjectMotionState.h | 9 +- .../physics/src/PhysicalEntitySimulation.cpp | 137 ++++++++++++------ .../physics/src/PhysicalEntitySimulation.h | 27 ++-- libraries/physics/src/PhysicsEngine.cpp | 64 +------- libraries/physics/src/PhysicsEngine.h | 8 +- libraries/physics/src/ShapeFactory.cpp | 4 + 20 files changed, 318 insertions(+), 486 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d84c3e8b85..4b10c23628 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2782,21 +2782,15 @@ Application::~Application() { // remove avatars from physics engine auto avatarManager = DependencyManager::get(); avatarManager->clearOtherAvatars(); + auto myCharacterController = getMyAvatar()->getCharacterController(); + myCharacterController->clearDetailedMotionStates(); PhysicsEngine::Transaction transaction; avatarManager->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); avatarManager->handleProcessedPhysicsTransaction(transaction); - avatarManager->deleteAllAvatars(); - auto myCharacterController = getMyAvatar()->getCharacterController(); - myCharacterController->clearDetailedMotionStates(); - - myCharacterController->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myCharacterController->handleProcessedPhysicsTransaction(transaction); - _physicsEngine->setCharacterController(nullptr); // the _shapeManager should have zero references @@ -6412,32 +6406,11 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "PrePhysics"); PerformanceTimer perfTimer("prePhysics)"); { - PROFILE_RANGE(simulation_physics, "RemoveEntities"); - const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); - { - PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); - _physicsEngine->removeObjects(motionStates); - } - _entitySimulation->deleteObjectsRemovedFromPhysics(); - } - - { - PROFILE_RANGE(simulation_physics, "AddEntities"); - VectorOfMotionStates motionStates; - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToAddToPhysics(motionStates); - PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); - _physicsEngine->addObjects(motionStates); - }); - } - { - VectorOfMotionStates motionStates; - PROFILE_RANGE(simulation_physics, "ChangeEntities"); - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToChange(motionStates); - VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); - _entitySimulation->setObjectsToChange(stillNeedChange); - }); + PROFILE_RANGE(simulation_physics, "Entities"); + PhysicsEngine::Transaction transaction; + _entitySimulation->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + _entitySimulation->handleProcessedPhysicsTransaction(transaction); } _entitySimulation->applyDynamicChanges(); @@ -6450,9 +6423,6 @@ void Application::update(float deltaTime) { avatarManager->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); avatarManager->handleProcessedPhysicsTransaction(transaction); - myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); myAvatar->prepareForPhysicsSimulation(); _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 00e743312f..8537217756 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,7 +101,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe } AvatarManager::~AvatarManager() { - assert(_avatarsToChangeInPhysics.empty()); + assert(_otherAvatarsToChangeInPhysics.empty()); } void AvatarManager::init() { @@ -314,7 +314,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // remove the orb if it is there avatar->removeOrb(); if (avatar->needsPhysicsUpdate()) { - _avatarsToChangeInPhysics.insert(avatar); + _otherAvatarsToChangeInPhysics.insert(avatar); } } else { avatar->updateOrbPosition(); @@ -419,69 +419,112 @@ AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) { } void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) { - _avatarsToChangeInPhysics.insert(avatar); + _otherAvatarsToChangeInPhysics.insert(avatar); +} + +DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex) { + bool isBound = false; + std::vector boundJoints; + const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints); + if (shape) { + //std::shared_ptr avatar = shared_from_this(); + //std::shared_ptr avatar = getThisPointer(); + DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); + motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok + motionState->setIsBound(isBound, boundJoints); + return motionState; + } + return nullptr; +} + +void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar) { + if (!avatar->_motionState) { + avatar->_motionState = new AvatarMotionState(avatar, nullptr); + } + AvatarMotionState* motionState = avatar->_motionState; + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + assert(shape); + motionState->setShape(shape); + motionState->setMass(avatar->computeMass()); + if (motionState->getRigidBody()) { + transaction.objectsToReinsert.push_back(motionState); + } else { + transaction.objectsToAdd.push_back(motionState); + } + motionState->clearIncomingDirtyFlags(); + + // Rather than reconcile numbers of joints after change to model or LOD + // we blow away old detailedMotionStates and create anew all around. + + // delete old detailedMotionStates + auto& detailedMotionStates = avatar->getDetailedMotionStates(); + if (detailedMotionStates.size() != 0) { + for (auto& detailedMotionState : detailedMotionStates) { + transaction.objectsToRemove.push_back(detailedMotionState); + } + avatar->resetDetailedMotionStates(); + } + + // build new detailedMotionStates + OtherAvatar::BodyLOD lod = avatar->getBodyLOD(); + if (lod == OtherAvatar::BodyLOD::Sphere) { + auto dMotionState = createDetailedMotionState(avatar, -1); + detailedMotionStates.push_back(dMotionState); + } else { + int32_t numJoints = avatar->getJointCount(); + for (int32_t i = 0; i < numJoints; i++) { + auto dMotionState = createDetailedMotionState(avatar, i); + if (dMotionState) { + detailedMotionStates.push_back(dMotionState); + } + } + } + avatar->_needsReinsertion = false; } void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - SetOfOtherAvatars failedShapeBuilds; - for (auto avatar : _avatarsToChangeInPhysics) { + _myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); + for (auto avatar : _otherAvatarsToChangeInPhysics) { bool isInPhysics = avatar->isInPhysicsSimulation(); if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) { if (isInPhysics) { transaction.objectsToRemove.push_back(avatar->_motionState); avatar->_motionState = nullptr; auto& detailedMotionStates = avatar->getDetailedMotionStates(); - for (auto& mState : detailedMotionStates) { - transaction.objectsToRemove.push_back(mState); + for (auto& motionState : detailedMotionStates) { + transaction.objectsToRemove.push_back(motionState); } avatar->resetDetailedMotionStates(); } else { - if (avatar->getDetailedMotionStates().size() == 0) { - avatar->createDetailedMotionStates(avatar); - for (auto dMotionState : avatar->getDetailedMotionStates()) { - transaction.objectsToAdd.push_back(dMotionState); - } - } - if (avatar->getDetailedMotionStates().size() > 0) { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); - motionState->setMass(avatar->computeMass()); - avatar->_motionState = motionState; - transaction.objectsToAdd.push_back(motionState); - } else { - failedShapeBuilds.insert(avatar); - } - } else { - failedShapeBuilds.insert(avatar); - } + rebuildAvatarPhysics(transaction, avatar); } } else if (isInPhysics) { - transaction.objectsToChange.push_back(avatar->_motionState); - - auto& detailedMotionStates = avatar->getDetailedMotionStates(); - for (auto& mState : detailedMotionStates) { - if (mState) { - transaction.objectsToChange.push_back(mState); + AvatarMotionState* motionState = avatar->_motionState; + if (motionState->needsNewShape()) { + rebuildAvatarPhysics(transaction, avatar); + } else { + uint32_t flags = motionState->getIncomingDirtyFlags(); + motionState->clearIncomingDirtyFlags(); + if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + motionState->handleEasyChanges(flags); + } + // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. + + if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); + for (auto detailedMotionState : avatar->getDetailedMotionStates()) { + transaction.objectsToReinsert.push_back(detailedMotionState); + } } } - } } - _avatarsToChangeInPhysics.swap(failedShapeBuilds); } void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - // things on objectsToChange correspond to failed changes - // so we push them back onto _avatarsToChangeInPhysics - for (auto object : transaction.objectsToChange) { - AvatarMotionState* motionState = static_cast(object); - assert(motionState); - assert(motionState->_avatar); - _avatarsToChangeInPhysics.insert(motionState->_avatar); - } // things on objectsToRemove are ready for delete for (auto object : transaction.objectsToRemove) { delete object; @@ -570,7 +613,7 @@ void AvatarManager::clearOtherAvatars() { ++avatarIterator; } } - } + } for (auto& av : removedAvatars) { handleRemovedAvatar(av); @@ -578,7 +621,7 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - assert(_avatarsToChangeInPhysics.empty()); + assert(_otherAvatarsToChangeInPhysics.empty()); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { @@ -588,7 +631,7 @@ void AvatarManager::deleteAllAvatars() { if (avatar != _myAvatar) { auto otherAvatar = std::static_pointer_cast(avatar); assert(!otherAvatar->_motionState); - assert(otherAvatar->getDetailedMotionStates().size() == 0); + assert(otherAvatar->getDetailedMotionStates().size() == 0); } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 98deebe919..db1bc125a4 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -273,6 +273,8 @@ public slots: protected: AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; + DetailedMotionState* createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex); + void rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar); private: explicit AvatarManager(QObject* parent = 0); @@ -288,7 +290,7 @@ private: void handleTransitAnimations(AvatarTransit::Status status); using SetOfOtherAvatars = std::set; - SetOfOtherAvatars _avatarsToChangeInPhysics; + SetOfOtherAvatars _otherAvatarsToChangeInPhysics; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 77fc81fa04..ae229dc66f 100755 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -29,17 +29,13 @@ void AvatarMotionState::handleEasyChanges(uint32_t& flags) { } } -bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - AvatarMotionState::~AvatarMotionState() { assert(_avatar); _avatar = nullptr; } // virtual -uint32_t AvatarMotionState::getIncomingDirtyFlags() { +uint32_t AvatarMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } @@ -54,13 +50,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { return MOTION_TYPE_DYNAMIC; } -// virtual and protected -const btCollisionShape* AvatarMotionState::computeNewShape() { - ShapeInfo shapeInfo; - _avatar->computeShapeInfo(shapeInfo); - return getShapeManager()->getShape(shapeInfo); -} - // virtual bool AvatarMotionState::isMoving() const { return false; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 3103341622..5f26b5114d 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -23,44 +23,44 @@ class AvatarMotionState : public ObjectMotionState { public: AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape); - virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; + void handleEasyChanges(uint32_t& flags) override; - virtual PhysicsMotionType getMotionType() const override { return _motionType; } + PhysicsMotionType getMotionType() const override { return _motionType; } - virtual uint32_t getIncomingDirtyFlags() override; - virtual void clearIncomingDirtyFlags() override; + uint32_t getIncomingDirtyFlags() const override; + void clearIncomingDirtyFlags() override; - virtual PhysicsMotionType computePhysicsMotionType() const override; + PhysicsMotionType computePhysicsMotionType() const override; - virtual bool isMoving() const override; + bool isMoving() const override; // this relays incoming position/rotation to the RigidBody - virtual void getWorldTransform(btTransform& worldTrans) const override; + void getWorldTransform(btTransform& worldTrans) const override; // this relays outgoing position/rotation to the EntityItem - virtual void setWorldTransform(const btTransform& worldTrans) override; + void setWorldTransform(const btTransform& worldTrans) override; // These pure virtual methods must be implemented for each MotionState type // and make it possible to implement more complicated methods in this base class. // pure virtual overrides from ObjectMotionState - virtual float getObjectRestitution() const override; - virtual float getObjectFriction() const override; - virtual float getObjectLinearDamping() const override; - virtual float getObjectAngularDamping() const override; + float getObjectRestitution() const override; + float getObjectFriction() const override; + float getObjectLinearDamping() const override; + float getObjectAngularDamping() const override; - virtual glm::vec3 getObjectPosition() const override; - virtual glm::quat getObjectRotation() const override; - virtual glm::vec3 getObjectLinearVelocity() const override; - virtual glm::vec3 getObjectAngularVelocity() const override; - virtual glm::vec3 getObjectGravity() const override; + glm::vec3 getObjectPosition() const override; + glm::quat getObjectRotation() const override; + glm::vec3 getObjectLinearVelocity() const override; + glm::vec3 getObjectAngularVelocity() const override; + glm::vec3 getObjectGravity() const override; - virtual const QUuid getObjectID() const override; + const QUuid getObjectID() const override; - virtual QString getName() const override; - virtual QUuid getSimulatorID() const override; + QString getName() const override; + ShapeType getShapeType() const override { return SHAPE_TYPE_CAPSULE_Y; } + QUuid getSimulatorID() const override; void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal); @@ -69,9 +69,9 @@ public: void setCollisionGroup(int32_t group) { _collisionGroup = group; } int32_t getCollisionGroup() { return _collisionGroup; } - virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; + void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; - virtual float getMass() const override; + float getMass() const override; friend class AvatarManager; friend class Avatar; @@ -85,9 +85,6 @@ protected: // ever called by the Avatar class dtor. ~AvatarMotionState(); - virtual bool isReadyToComputeShape() const override { return true; } - virtual const btCollisionShape* computeNewShape() override; - OtherAvatarPointer _avatar; float _diameter { 0.0f }; int32_t _collisionGroup; diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index cec27108ca..4cfbbf032c 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -17,7 +17,7 @@ #include "MyAvatar.h" -DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex) : +DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int32_t jointIndex) : ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) { assert(_avatar); if (!_avatar->isMyAvatar()) { @@ -33,17 +33,13 @@ void DetailedMotionState::handleEasyChanges(uint32_t& flags) { } } -bool DetailedMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - DetailedMotionState::~DetailedMotionState() { assert(_avatar); _avatar = nullptr; } // virtual -uint32_t DetailedMotionState::getIncomingDirtyFlags() { +uint32_t DetailedMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } @@ -54,26 +50,9 @@ void DetailedMotionState::clearIncomingDirtyFlags() { } PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const { - // TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting) return MOTION_TYPE_KINEMATIC; } -// virtual and protected -const btCollisionShape* DetailedMotionState::computeNewShape() { - btCollisionShape* shape = nullptr; - if (!_avatar->isMyAvatar()) { - if (_otherAvatar != nullptr) { - shape = _otherAvatar->createCollisionShape(_jointIndex, _isBound, _boundJoints); - } - } else { - std::shared_ptr myAvatar = std::static_pointer_cast(_avatar); - if (myAvatar) { - shape = myAvatar->getCharacterController()->createDetailedCollisionShapeForJoint(_jointIndex); - } - } - return shape; -} - // virtual bool DetailedMotionState::isMoving() const { return false; @@ -185,4 +164,4 @@ void DetailedMotionState::forceActive() { if (_body && !_body->isActive()) { _body->setActivationState(ACTIVE_TAG); } -} \ No newline at end of file +} diff --git a/interface/src/avatar/DetailedMotionState.h b/interface/src/avatar/DetailedMotionState.h index a9b4b4bb64..95b0600cf9 100644 --- a/interface/src/avatar/DetailedMotionState.h +++ b/interface/src/avatar/DetailedMotionState.h @@ -23,55 +23,55 @@ class DetailedMotionState : public ObjectMotionState { public: DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex); - virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; + void handleEasyChanges(uint32_t& flags) override; - virtual PhysicsMotionType getMotionType() const override { return _motionType; } + PhysicsMotionType getMotionType() const override { return _motionType; } - virtual uint32_t getIncomingDirtyFlags() override; - virtual void clearIncomingDirtyFlags() override; + uint32_t getIncomingDirtyFlags() const override; + void clearIncomingDirtyFlags() override; - virtual PhysicsMotionType computePhysicsMotionType() const override; + PhysicsMotionType computePhysicsMotionType() const override; - virtual bool isMoving() const override; + bool isMoving() const override; // this relays incoming position/rotation to the RigidBody - virtual void getWorldTransform(btTransform& worldTrans) const override; + void getWorldTransform(btTransform& worldTrans) const override; // this relays outgoing position/rotation to the EntityItem - virtual void setWorldTransform(const btTransform& worldTrans) override; + void setWorldTransform(const btTransform& worldTrans) override; // These pure virtual methods must be implemented for each MotionState type // and make it possible to implement more complicated methods in this base class. // pure virtual overrides from ObjectMotionState - virtual float getObjectRestitution() const override; - virtual float getObjectFriction() const override; - virtual float getObjectLinearDamping() const override; - virtual float getObjectAngularDamping() const override; + float getObjectRestitution() const override; + float getObjectFriction() const override; + float getObjectLinearDamping() const override; + float getObjectAngularDamping() const override; - virtual glm::vec3 getObjectPosition() const override; - virtual glm::quat getObjectRotation() const override; - virtual glm::vec3 getObjectLinearVelocity() const override; - virtual glm::vec3 getObjectAngularVelocity() const override; - virtual glm::vec3 getObjectGravity() const override; + glm::vec3 getObjectPosition() const override; + glm::quat getObjectRotation() const override; + glm::vec3 getObjectLinearVelocity() const override; + glm::vec3 getObjectAngularVelocity() const override; + glm::vec3 getObjectGravity() const override; - virtual const QUuid getObjectID() const override; + const QUuid getObjectID() const override; - virtual QString getName() const override; - virtual QUuid getSimulatorID() const override; + QString getName() const override; + ShapeType getShapeType() const override { return SHAPE_TYPE_HULL; } + QUuid getSimulatorID() const override; void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } - virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; + void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; - virtual float getMass() const override; + float getMass() const override; void forceActive(); QUuid getAvatarID() const { return _avatar->getID(); } - int getJointIndex() const { return _jointIndex; } - void setIsBound(bool isBound, std::vector boundJoints) { _isBound = isBound; _boundJoints = boundJoints; } - bool getIsBound(std::vector& boundJoints) const { boundJoints = _boundJoints; return _isBound; } + int32_t getJointIndex() const { return _jointIndex; } + void setIsBound(bool isBound, const std::vector& boundJoints) { _isBound = isBound; _boundJoints = boundJoints; } + bool getIsBound(std::vector& boundJoints) const { boundJoints = _boundJoints; return _isBound; } friend class AvatarManager; friend class Avatar; @@ -84,17 +84,14 @@ protected: // ever called by the Avatar class dtor. ~DetailedMotionState(); - virtual bool isReadyToComputeShape() const override { return true; } - virtual const btCollisionShape* computeNewShape() override; - AvatarPointer _avatar; float _diameter { 0.0f }; uint32_t _dirtyFlags; - int _jointIndex { -1 }; + int32_t _jointIndex { -1 }; OtherAvatarPointer _otherAvatar { nullptr }; bool _isBound { false }; - std::vector _boundJoints; + std::vector _boundJoints; }; #endif // hifi_DetailedMotionState_h diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index b0123abe8d..aef1bcd668 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -377,21 +377,18 @@ void MyCharacterController::updateMassProperties() { _rigidBody->setMassProps(mass, inertia); } -btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int jointIndex) { +const btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int32_t jointIndex) { ShapeInfo shapeInfo; _avatar->computeDetailedShapeInfo(shapeInfo, jointIndex); if (shapeInfo.getType() != SHAPE_TYPE_NONE) { - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - shape->setMargin(0.001f); - } + const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); return shape; } return nullptr; } -DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int jointIndex) { - auto shape = createDetailedCollisionShapeForJoint(jointIndex); +DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int32_t jointIndex) { + const btCollisionShape* shape = createDetailedCollisionShapeForJoint(jointIndex); if (shape) { DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex); motionState->setMass(_avatar->computeMass()); @@ -423,25 +420,16 @@ void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& } if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) { _pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; - for (int i = 0; i < _avatar->getJointCount(); i++) { + for (int32_t i = 0; i < _avatar->getJointCount(); i++) { auto dMotionState = createDetailedMotionStateForJoint(i); if (dMotionState) { _detailedMotionStates.push_back(dMotionState); transaction.objectsToAdd.push_back(dMotionState); } } - } -} - -void MyCharacterController::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - // things on objectsToRemove are ready for delete - for (auto object : transaction.objectsToRemove) { - delete object; } - transaction.clear(); } - class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback { public: DetailedRayResultCallback() @@ -467,7 +455,7 @@ std::vector MyCharacterController::rayTe _dynamicsWorld->rayTest(origin, end, rayCallback); if (rayCallback.m_hitFractions.size() > 0) { foundAvatars.reserve(rayCallback.m_hitFractions.size()); - for (int i = 0; i < rayCallback.m_hitFractions.size(); i++) { + for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) { auto object = rayCallback.m_collisionObjects[i]; ObjectMotionState* motionState = static_cast(object->getUserPointer()); if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) { @@ -493,4 +481,4 @@ std::vector MyCharacterController::rayTe } } return foundAvatars; -} \ No newline at end of file +} diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index f861f82422..0b64f66850 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -44,27 +44,25 @@ public: void setDensity(btScalar density) { _density = density; } - btCollisionShape* createDetailedCollisionShapeForJoint(int jointIndex); - DetailedMotionState* createDetailedMotionStateForJoint(int jointIndex); + const btCollisionShape* createDetailedCollisionShapeForJoint(int32_t jointIndex); + DetailedMotionState* createDetailedMotionStateForJoint(int32_t jointIndex); std::vector& getDetailedMotionStates() { return _detailedMotionStates; } void clearDetailedMotionStates(); void resetDetailedMotionStates(); void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); - void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); - struct RayAvatarResult { bool _intersect { false }; bool _isBound { false }; QUuid _intersectWithAvatar; - int _intersectWithJoint { -1 }; + int32_t _intersectWithJoint { -1 }; float _distance { 0.0f }; float _maxDistance { 0.0f }; QVariantMap _extraInfo; glm::vec3 _intersectionPoint; glm::vec3 _intersectionNormal; - std::vector _boundJoints; + std::vector _boundJoints; }; std::vector rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length, const QVector& jointsToExclude) const; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index d8cfe8f107..107932d5ec 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -116,6 +116,8 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); for (size_t i = 0; i < _detailedMotionStates.size(); i++) { + // NOTE: we activate _detailedMotionStates is because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() when active. _detailedMotionStates[i]->forceActive(); } if (_moving && _motionState) { @@ -124,11 +126,11 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { return bytesRead; } -btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBound, std::vector& boundJoints) { +const btCollisionShape* OtherAvatar::createCollisionShape(int32_t jointIndex, bool& isBound, std::vector& boundJoints) { ShapeInfo shapeInfo; isBound = false; - QString jointName = ""; - if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) { + QString jointName = ""; + if (jointIndex > -1 && jointIndex < (int32_t)_multiSphereShapes.size()) { jointName = _multiSphereShapes[jointIndex].getJointName(); } switch (_bodyLOD) { @@ -163,39 +165,21 @@ btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBoun } break; } + // Note: MultiSphereLow case really means: "skip fingers and use spheres for hands, + // else fall through to MultiSphereHigh case" case BodyLOD::MultiSphereHigh: computeDetailedShapeInfo(shapeInfo, jointIndex); break; default: + assert(false); // should never reach here break; } - if (shapeInfo.getType() != SHAPE_TYPE_NONE) { - auto shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - shape->setMargin(0.001f); - } - return shape; - } - return nullptr; -} - -DetailedMotionState* OtherAvatar::createMotionState(std::shared_ptr avatar, int jointIndex) { - bool isBound = false; - std::vector boundJoints; - btCollisionShape* shape = createCollisionShape(jointIndex, isBound, boundJoints); - if (shape) { - DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); - motionState->setMass(computeMass()); - motionState->setIsBound(isBound, boundJoints); - return motionState; - } - return nullptr; + return ObjectMotionState::getShapeManager()->getShape(shapeInfo); } void OtherAvatar::resetDetailedMotionStates() { - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - _detailedMotionStates[i] = nullptr; - } + // NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove + // See AvatarManager::handleProcessedPhysicsTransaction() _detailedMotionStates.clear(); } @@ -231,11 +215,11 @@ void OtherAvatar::computeShapeLOD() { } bool OtherAvatar::isInPhysicsSimulation() const { - return _motionState != nullptr && _detailedMotionStates.size() > 0; + return _motionState && _motionState->getRigidBody(); } bool OtherAvatar::shouldBeInPhysicsSimulation() const { - return !isDead() && !(isInPhysicsSimulation() && _needsReinsertion); + return !isDead() && _workloadRegion < workload::Region::R3; } bool OtherAvatar::needsPhysicsUpdate() const { @@ -245,12 +229,9 @@ bool OtherAvatar::needsPhysicsUpdate() const { void OtherAvatar::rebuildCollisionShape() { if (_motionState) { + // do not actually rebuild here, instead flag for later _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); - } - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - if (_detailedMotionStates[i]) { - _detailedMotionStates[i]->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); - } + _needsReinsertion = true; } } @@ -260,25 +241,6 @@ void OtherAvatar::setCollisionWithOtherAvatarsFlags() { } } -void OtherAvatar::createDetailedMotionStates(const std::shared_ptr& avatar) { - auto& detailedMotionStates = getDetailedMotionStates(); - assert(detailedMotionStates.empty()); - if (_bodyLOD == BodyLOD::Sphere) { - auto dMotionState = createMotionState(avatar, -1); - if (dMotionState) { - detailedMotionStates.push_back(dMotionState); - } - } else { - for (int i = 0; i < getJointCount(); i++) { - auto dMotionState = createMotionState(avatar, i); - if (dMotionState) { - detailedMotionStates.push_back(dMotionState); - } - } - } - _needsReinsertion = false; -} - void OtherAvatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 43bfd2a9ae..498971d6ee 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -52,9 +52,7 @@ public: bool shouldBeInPhysicsSimulation() const; bool needsPhysicsUpdate() const; - btCollisionShape* createCollisionShape(int jointIndex, bool& isBound, std::vector& boundJoints); - DetailedMotionState* createMotionState(std::shared_ptr avatar, int jointIndex); - void createDetailedMotionStates(const std::shared_ptr& avatar); + const btCollisionShape* createCollisionShape(int32_t jointIndex, bool& isBound, std::vector& boundJoints); std::vector& getDetailedMotionStates() { return _detailedMotionStates; } void resetDetailedMotionStates(); BodyLOD getBodyLOD() { return _bodyLOD; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6abe5c3899..24ce17d6fb 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -25,23 +25,6 @@ #include "PhysicsHelpers.h" #include "PhysicsLogging.h" -#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS -#include "EntityTree.h" - -bool EntityMotionState::entityTreeIsLocked() const { - EntityTreeElementPointer element = _entity->getElement(); - EntityTreePointer tree = element ? element->getTree() : nullptr; - if (!tree) { - return true; - } - return true; -} -#else -bool entityTreeIsLocked() { - return true; -} -#endif - const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -74,7 +57,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); - assert(entityTreeIsLocked()); setMass(_entity->computeMass()); // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here // rather than pass the legit shape pointer to the ObjectMotionState ctor above. @@ -143,7 +125,6 @@ void EntityMotionState::handleDeactivation() { // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { - assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); @@ -191,17 +172,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { } -// virtual -bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - updateServerPhysicsVariables(); - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { if (!_entity) { return MOTION_TYPE_STATIC; } - assert(entityTreeIsLocked()); if (_entity->getLocked()) { if (_entity->isMoving()) { @@ -226,7 +200,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { } bool EntityMotionState::isMoving() const { - assert(entityTreeIsLocked()); return _entity && _entity->isMovingRelativeToParent(); } @@ -240,7 +213,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (!_entity) { return; } - assert(entityTreeIsLocked()); if (_motionType == MOTION_TYPE_KINEMATIC) { BT_PROFILE("kinematicIntegration"); uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); @@ -271,7 +243,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { // This callback is invoked by the physics simulation at the end of each simulation step... // iff the corresponding RigidBody is DYNAMIC and ACTIVE. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { - assert(entityTreeIsLocked()); measureBodyAcceleration(); // If transform or velocities are flagged as dirty it means a network or scripted change @@ -309,19 +280,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { } -// virtual and protected -bool EntityMotionState::isReadyToComputeShape() const { - return _entity->isReadyToComputeShape(); -} - -// virtual and protected -const btCollisionShape* EntityMotionState::computeNewShape() { - ShapeInfo shapeInfo; - assert(entityTreeIsLocked()); - _entity->computeShapeInfo(shapeInfo); - return getShapeManager()->getShape(shapeInfo); -} - const uint8_t MAX_NUM_INACTIVE_UPDATES = 20; bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { @@ -439,7 +397,6 @@ 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(entityTreeIsLocked()); // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); @@ -505,7 +462,6 @@ void EntityMotionState::updateSendVelocities() { void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) { DETAILED_PROFILE_RANGE(simulation_physics, "Bid"); - assert(entityTreeIsLocked()); updateSendVelocities(); @@ -546,7 +502,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { DETAILED_PROFILE_RANGE(simulation_physics, "Send"); - assert(entityTreeIsLocked()); assert(isLocallyOwned()); updateSendVelocities(); @@ -645,8 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _bumpedPriority = 0; } -uint32_t EntityMotionState::getIncomingDirtyFlags() { - assert(entityTreeIsLocked()); +uint32_t EntityMotionState::getIncomingDirtyFlags() const { uint32_t dirtyFlags = 0; if (_body && _entity) { dirtyFlags = _entity->getDirtyFlags(); @@ -677,7 +631,6 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { } void EntityMotionState::clearIncomingDirtyFlags() { - assert(entityTreeIsLocked()); if (_body && _entity) { _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); } @@ -694,7 +647,6 @@ void EntityMotionState::slaveBidPriority() { // virtual QUuid EntityMotionState::getSimulatorID() const { - assert(entityTreeIsLocked()); return _entity->getSimulatorID(); } @@ -762,6 +714,10 @@ glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { return _measuredAcceleration * _measuredDeltaTime; } +bool EntityMotionState::shouldBeInPhysicsSimulation() const { + return _region < workload::Region::R3 && _entity->shouldBePhysical(); +} + // virtual void EntityMotionState::setMotionType(PhysicsMotionType motionType) { ObjectMotionState::setMotionType(motionType); @@ -770,7 +726,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) { // virtual QString EntityMotionState::getName() const { - assert(entityTreeIsLocked()); return _entity->getName(); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 6e1ea66685..0d5a97eeb8 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -39,7 +39,6 @@ public: void handleDeactivation(); virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; /// \return PhysicsMotionType based on params set in EntityItem virtual PhysicsMotionType computePhysicsMotionType() const override; @@ -56,7 +55,7 @@ public: void sendBid(OctreeEditPacketSender* packetSender, uint32_t step); void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); - virtual uint32_t getIncomingDirtyFlags() override; + virtual uint32_t getIncomingDirtyFlags() const override; virtual void clearIncomingDirtyFlags() override; virtual float getObjectRestitution() const override { return _entity->getRestitution(); } @@ -85,6 +84,7 @@ public: void measureBodyAcceleration(); virtual QString getName() const override; + ShapeType getShapeType() const override { return _entity->getShapeType(); } virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; @@ -113,12 +113,8 @@ protected: void clearObjectVelocities() const; - #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS - bool entityTreeIsLocked() const; - #endif - - bool isReadyToComputeShape() const override; - const btCollisionShape* computeNewShape() override; + bool isInPhysicsSimulation() const { return _body != nullptr; } + bool shouldBeInPhysicsSimulation() const; void setMotionType(PhysicsMotionType motionType) override; // EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 0ab051fa96..92482437e2 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -201,6 +201,9 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { if (_body && _type != MOTIONSTATE_TYPE_DETAILED) { updateCCDConfiguration(); } + } else if (shape) { + // we need to release unused reference to shape + getShapeManager()->releaseShape(shape); } } @@ -285,50 +288,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { } } -bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - assert(_body && _shape); - if (flags & Simulation::DIRTY_SHAPE) { - // make sure the new shape is valid - if (!isReadyToComputeShape()) { - return false; - } - const btCollisionShape* newShape = computeNewShape(); - if (!newShape) { - qCDebug(physics) << "Warning: failed to generate new shape!"; - // failed to generate new shape! --> keep old shape and remove shape-change flag - flags &= ~Simulation::DIRTY_SHAPE; - // TODO: force this object out of PhysicsEngine rather than just use the old shape - if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) { - // no HARD flags remain, so do any EASY changes - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); - } - return true; - } - } else { - if (_shape == newShape) { - // the shape didn't actually change, so we clear the DIRTY_SHAPE flag - flags &= ~Simulation::DIRTY_SHAPE; - // and clear the reference we just created - getShapeManager()->releaseShape(_shape); - } else { - _body->setCollisionShape(const_cast(newShape)); - setShape(newShape); - } - } - } - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); - } - // it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed) - // so we check again before we reinsert: - if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - engine->reinsertObject(this); - } - - return true; -} - void ObjectMotionState::updateBodyMaterialProperties() { _body->setRestitution(getObjectRestitution()); _body->setFriction(getObjectFriction()); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index a53215753e..7d204194b2 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -100,7 +100,6 @@ public: virtual ~ObjectMotionState(); virtual void handleEasyChanges(uint32_t& flags); - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); void updateBodyVelocities(); @@ -123,11 +122,12 @@ public: glm::vec3 getBodyAngularVelocity() const; virtual glm::vec3 getObjectLinearVelocityChange() const; - virtual uint32_t getIncomingDirtyFlags() = 0; + virtual uint32_t getIncomingDirtyFlags() const = 0; virtual void clearIncomingDirtyFlags() = 0; virtual PhysicsMotionType computePhysicsMotionType() const = 0; + virtual bool needsNewShape() const { return _shape == nullptr || getIncomingDirtyFlags() & Simulation::DIRTY_SHAPE; } const btCollisionShape* getShape() const { return _shape; } btRigidBody* getRigidBody() const { return _body; } @@ -154,6 +154,7 @@ public: virtual void bump(uint8_t priority) {} virtual QString getName() const { return ""; } + virtual ShapeType getShapeType() const = 0; virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0; @@ -172,8 +173,6 @@ public: friend class PhysicsEngine; protected: - virtual bool isReadyToComputeShape() const = 0; - virtual const btCollisionShape* computeNewShape() = 0; virtual void setMotionType(PhysicsMotionType motionType); void updateCCDConfiguration(); @@ -187,7 +186,7 @@ protected: btRigidBody* _body { nullptr }; float _density { 1.0f }; - // ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These date members allow an Action + // ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These data members allow an Action // to operate on a kinematic object without screwing up our default kinematic integration // which is done in the MotionState::getWorldTransform(). mutable uint32_t _lastKinematicStep; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 515a99929c..10b5532cd8 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -227,56 +227,16 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) } // end EntitySimulation overrides -const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() { - QMutexLocker lock(&_mutex); - for (auto entity: _entitiesToRemoveFromPhysics) { - _entitiesToAddToPhysics.remove(entity); - if (entity->isDead() && entity->getElement()) { - _deadEntities.insert(entity); - } - - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - _incomingChanges.remove(motionState); - removeOwnershipData(motionState); - _physicalObjects.remove(motionState); - // remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine) - _objectsToDelete.push_back(motionState); - } - } - _entitiesToRemoveFromPhysics.clear(); - return _objectsToDelete; -} - -void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() { - QMutexLocker lock(&_mutex); - for (auto motionState : _objectsToDelete) { - // someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo - // until then we must do it here - // NOTE: a reference to the EntityItemPointer is released in the EntityMotionState::dtor - delete motionState; - } - _objectsToDelete.clear(); -} - -void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) { - result.clear(); - +void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { // this lambda for when we decide to actually build the motionState auto buildMotionState = [&](btCollisionShape* shape, EntityItemPointer entity) { EntityMotionState* motionState = new EntityMotionState(shape, entity); entity->setPhysicsInfo(static_cast(motionState)); motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); _physicalObjects.insert(motionState); - result.push_back(motionState); + _incomingChanges.insert(motionState); }; - // TODO: - // (1) make all changes to MotionState in "managers" (PhysicalEntitySimulation and AvatarManager) - // (2) store relevant change-flags on MotionState (maybe just EASY or HARD?) - // (3) remove knowledge of PhysicsEngine from ObjectMotionState - // (4) instead PhysicsEngine gets list of changed MotionStates, reads change-flags and applies changes accordingly - QMutexLocker lock(&_mutex); uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); if (deliveryCount != _lastWorkDeliveryCount) { @@ -319,7 +279,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } else { // this is a CHANGE because motionState already exists if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) { - // TODO? reset DIRTY_SHAPE flag? + entity->markDirtyFlags(Simulation::DIRTY_SHAPE); _incomingChanges.insert(motionState); requestItr = _shapeRequests.erase(requestItr); } else { @@ -338,7 +298,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re prepareEntityForDelete(entity); entityItr = _entitiesToAddToPhysics.erase(entityItr); } else if (!entity->shouldBePhysical()) { - // this entity should no longer be on the internal _entitiesToAddToPhysics + // this entity should no longer be on _entitiesToAddToPhysics entityItr = _entitiesToAddToPhysics.erase(entityItr); if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); @@ -347,16 +307,27 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } } } else if (entity->isReadyToComputeShape()) { - // check to see if we're waiting for a shape ShapeRequest shapeRequest(entity); ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); if (requestItr == _shapeRequests.end()) { + // not waiting for a shape (yet) ShapeInfo shapeInfo; entity->computeShapeInfo(shapeInfo); uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { - buildMotionState(shape, entity); + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (!motionState) { + buildMotionState(shape, entity); + } else { + // Is it possible to fall in here? + // entity shouldn't be on _entitiesToAddToPhysics list if it already has a motionState. + // but just in case... + motionState->setShape(shape); + motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); + _physicalObjects.insert(motionState); + _incomingChanges.insert(motionState); + } } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait shapeRequest.shapeHash = shapeInfo.getHash(); @@ -389,6 +360,80 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _incomingChanges.clear(); } +void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + buildMotionStatesForEntitiesThatNeedThem(); + for (auto& object : _incomingChanges) { + uint32_t flags = object->getIncomingDirtyFlags(); + object->clearIncomingDirtyFlags(); + + bool isInPhysicsSimulation = object->isInPhysicsSimulation(); + if (isInPhysicsSimulation != object->shouldBeInPhysicsSimulation()) { + if (isInPhysicsSimulation) { + transaction.objectsToRemove.push_back(object); + continue; + } else { + transaction.objectsToAdd.push_back(object); + } + } + + bool reinsert = false; + if (object->needsNewShape()) { + ShapeType shapeType = object->getShapeType(); + if (shapeType == SHAPE_TYPE_STATIC_MESH) { + ShapeRequest shapeRequest(object->_entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + ShapeInfo shapeInfo; + object->_entity->computeShapeInfo(shapeInfo); + uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + object->setShape(shape); + reinsert = true; + } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { + // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait + shapeRequest.shapeHash = shapeInfo.getHash(); + _shapeRequests.insert(shapeRequest); + } else { + // failed to build shape --> will not be added/updated + } + } else { + // continue waiting for shape request + } + } else { + ShapeInfo shapeInfo; + object->_entity->computeShapeInfo(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + object->setShape(shape); + reinsert = true; + } else { + // failed to build shape --> will not be added + } + } + } + if (!isInPhysicsSimulation) { + continue; + } + if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(flags); + } + if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP) || reinsert) { + transaction.objectsToReinsert.push_back(object); + } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { + transaction.activeStaticObjects.push_back(object); + } + } +} + +void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + delete object; + } + transaction.clear(); +} + void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) { bool serverlessMode = getEntityTree()->isServerlessMode(); for (auto stateItr : motionStates) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 65a2b8f90d..d7d8adffac 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -80,13 +81,12 @@ protected: // only called by EntitySimulation public: virtual void prepareEntityForDelete(EntityItemPointer entity) override; - const VectorOfMotionStates& getObjectsToRemoveFromPhysics(); - void deleteObjectsRemovedFromPhysics(); - - void getObjectsToAddToPhysics(VectorOfMotionStates& result); void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); + void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates); void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); @@ -99,23 +99,20 @@ public: void sendOwnedUpdates(uint32_t numSubsteps); private: + void buildMotionStatesForEntitiesThatNeedThem(); + class ShapeRequest { public: - ShapeRequest() : entity(), shapeHash(0) {} - ShapeRequest(const EntityItemPointer& e) : entity(e), shapeHash(0) {} + ShapeRequest() { } + ShapeRequest(const EntityItemPointer& e) : entity(e) { } bool operator<(const ShapeRequest& other) const { return entity.get() < other.entity.get(); } bool operator==(const ShapeRequest& other) const { return entity.get() == other.entity.get(); } - EntityItemPointer entity; - mutable uint64_t shapeHash; + EntityItemPointer entity { nullptr }; + mutable uint64_t shapeHash { 0 }; }; - SetOfEntities _entitiesToAddToPhysics; + SetOfEntities _entitiesToAddToPhysics; // we could also call this: _entitiesThatNeedMotionStates SetOfEntities _entitiesToRemoveFromPhysics; - - VectorOfMotionStates _objectsToDelete; - - SetOfEntityMotionStates _incomingChanges; // EntityMotionStates that have changed from external sources - // and need their RigidBodies updated - + SetOfEntityMotionStates _incomingChanges; // EntityMotionStates changed by external events SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine using ShapeRequests = std::set; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 4453a7d9f0..bae9ef2485 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -260,35 +260,6 @@ void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) { } } -VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& objects) { - VectorOfMotionStates stillNeedChange; - for (auto object : objects) { - uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; - if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - if (object->handleHardAndEasyChanges(flags, this)) { - object->clearIncomingDirtyFlags(); - } else { - stillNeedChange.push_back(object); - } - } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); - object->clearIncomingDirtyFlags(); - } - if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { - _activeStaticBodies.insert(object->getRigidBody()); - } - } - // active static bodies have changed (in an Easy way) and need their Aabbs updated - // but we've configured Bullet to NOT update them automatically (for improved performance) - // so we must do it ourselves - std::set::const_iterator itr = _activeStaticBodies.begin(); - while (itr != _activeStaticBodies.end()) { - _dynamicsWorld->updateSingleAabb(*itr); - ++itr; - } - return stillNeedChange; -} - void PhysicsEngine::reinsertObject(ObjectMotionState* object) { // remove object from DynamicsWorld bumpAndPruneContacts(object); @@ -320,7 +291,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) body->setMotionState(nullptr); delete body; } - object->clearIncomingDirtyFlags(); } // adds @@ -328,34 +298,16 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) addObjectToDynamicsWorld(object); } - // changes - std::vector failedChanges; - for (auto object : transaction.objectsToChange) { - uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; - if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - if (object->handleHardAndEasyChanges(flags, this)) { - object->clearIncomingDirtyFlags(); - } else { - failedChanges.push_back(object); - } - } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); - object->clearIncomingDirtyFlags(); - } - if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { - _activeStaticBodies.insert(object->getRigidBody()); - } + // reinserts + for (auto object : transaction.objectsToReinsert) { + reinsertObject(object); } - // activeStaticBodies have changed (in an Easy way) and need their Aabbs updated - // but we've configured Bullet to NOT update them automatically (for improved performance) - // so we must do it ourselves - std::set::const_iterator itr = _activeStaticBodies.begin(); - while (itr != _activeStaticBodies.end()) { - _dynamicsWorld->updateSingleAabb(*itr); - ++itr; + + for (auto object : transaction.activeStaticObjects) { + btRigidBody* body = object->getRigidBody(); + _dynamicsWorld->updateSingleAabb(body); + _activeStaticBodies.insert(body); } - // we replace objectsToChange with any that failed - transaction.objectsToChange.swap(failedChanges); } void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 43cc0d2176..d11b52f1af 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -79,11 +79,13 @@ public: void clear() { objectsToRemove.clear(); objectsToAdd.clear(); - objectsToChange.clear(); + objectsToReinsert.clear(); + activeStaticObjects.clear(); } std::vector objectsToRemove; std::vector objectsToAdd; - std::vector objectsToChange; + std::vector objectsToReinsert; + std::vector activeStaticObjects; }; PhysicsEngine(const glm::vec3& offset); @@ -97,7 +99,7 @@ public: void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown void addObjects(const VectorOfMotionStates& objects); - VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); + void changeObjects(const VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); void processTransaction(Transaction& transaction); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 9e0103e859..ef5213df8f 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -293,6 +293,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) radiuses.push_back(sphereData.w); } shape = new btMultiSphereShape(positions.data(), radiuses.data(), (int)positions.size()); + const float MULTI_SPHERE_MARGIN = 0.001f; + shape->setMargin(MULTI_SPHERE_MARGIN); } break; case SHAPE_TYPE_ELLIPSOID: { @@ -459,6 +461,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = compound; } } + } else { + // TODO: warn about this case } return shape; } From 915cbb69dfb96f4aea1bd4f27661749cf50ec6c7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 26 Apr 2019 11:55:50 -0700 Subject: [PATCH 25/83] split work out of EntityTree::update(), call it preUpdate() --- .../src/entities/EntityTreeHeadlessViewer.cpp | 1 + interface/src/Application.cpp | 2 + .../src/EntityTreeRenderer.cpp | 6 ++ .../src/EntityTreeRenderer.h | 1 + libraries/entities/src/EntitySimulation.cpp | 57 ++++++++++++------- libraries/entities/src/EntitySimulation.h | 7 ++- libraries/entities/src/EntityTree.cpp | 10 +++- libraries/entities/src/EntityTree.h | 7 ++- .../entities/src/SimpleEntitySimulation.cpp | 4 +- .../entities/src/SimpleEntitySimulation.h | 2 +- libraries/octree/src/Octree.h | 5 +- libraries/octree/src/OctreePersistThread.cpp | 1 + .../physics/src/PhysicalEntitySimulation.cpp | 6 +- .../physics/src/PhysicalEntitySimulation.h | 2 +- 14 files changed, 76 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp b/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp index 3649cf1129..5f43627763 100644 --- a/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp +++ b/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp @@ -37,6 +37,7 @@ void EntityTreeHeadlessViewer::update() { if (_tree) { EntityTreePointer tree = std::static_pointer_cast(_tree); tree->withTryWriteLock([&] { + tree->preUpdate(); tree->update(); }); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4b10c23628..1474a954fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6399,6 +6399,8 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "Simulation"); PerformanceTimer perfTimer("simulation"); + getEntities()->preUpdate(); + if (_physicsEnabled) { auto t0 = std::chrono::high_resolution_clock::now(); auto t1 = t0; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7d130125b3..7953b7f4bc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -474,6 +474,12 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } } +void EntityTreeRenderer::preUpdate() { + if (_tree && !_shuttingDown) { + _tree->preUpdate(); + } +} + void EntityTreeRenderer::update(bool simulate) { PROFILE_RANGE(simulation_physics, "ETR::update"); PerformanceTimer perfTimer("ETRupdate"); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 05105a2c7f..9391c47b5f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -78,6 +78,7 @@ public: void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; } void shutdown(); + void preUpdate(); void update(bool simulate); EntityTreePointer getTree() { return std::static_pointer_cast(_tree); } diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 05e71a8f06..e0a6d7af7c 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -176,17 +176,26 @@ void EntitySimulation::addEntity(EntityItemPointer entity) { void EntitySimulation::changeEntity(EntityItemPointer entity) { QMutexLocker lock(&_mutex); assert(entity); - if (!entity->isSimulated()) { - // This entity was either never added to the simulation or has been removed - // (probably for pending delete), so we don't want to keep a pointer to it - // on any internal lists. - return; - } + _changedEntities.insert(entity); +} +void EntitySimulation::processChangedEntities() { + QMutexLocker lock(&_mutex); + PROFILE_RANGE_EX(simulation_physics, "processChangedEntities", 0xffff00ff, (uint64_t)_changedEntities.size()); + for (auto& entity : _changedEntities) { + if (entity->isSimulated()) { + processChangedEntity(entity); + } + } + _changedEntities.clear(); +} + +void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) { + uint32_t dirtyFlags = entity->getDirtyFlags(); + /* TODO? maybe add to _entitiesToSort when DIRTY_POSITION is set? // Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes // it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag. - uint32_t dirtyFlags = entity->getDirtyFlags(); if (dirtyFlags & Simulation::DIRTY_POSITION) { AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); bool success; @@ -198,25 +207,29 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { return; } } + */ - if (dirtyFlags & Simulation::DIRTY_LIFETIME) { - if (entity->isMortal()) { - _mortalEntities.insert(entity); - uint64_t expiry = entity->getExpiry(); - if (expiry < _nextExpiry) { - _nextExpiry = expiry; + if (dirtyFlags & (Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE)) { + if (dirtyFlags & Simulation::DIRTY_LIFETIME) { + if (entity->isMortal()) { + _mortalEntities.insert(entity); + uint64_t expiry = entity->getExpiry(); + if (expiry < _nextExpiry) { + _nextExpiry = expiry; + } + } else { + _mortalEntities.remove(entity); } - } else { - _mortalEntities.remove(entity); } - entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME); + if (dirtyFlags & Simulation::DIRTY_UPDATEABLE) { + if (entity->needsToCallUpdate()) { + _entitiesToUpdate.insert(entity); + } else { + _entitiesToUpdate.remove(entity); + } + } + entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE); } - if (entity->needsToCallUpdate()) { - _entitiesToUpdate.insert(entity); - } else { - _entitiesToUpdate.remove(entity); - } - changeEntityInternal(entity); } void EntitySimulation::clearEntities() { diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index f107bcae6e..1dd0369561 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -13,6 +13,7 @@ #define hifi_EntitySimulation_h #include +#include #include #include @@ -82,13 +83,15 @@ public: /// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others. virtual void prepareEntityForDelete(EntityItemPointer entity); + void processChangedEntities(); + protected: // These pure virtual methods are protected because they are not to be called will-nilly. The base class // calls them in the right places. virtual void updateEntitiesInternal(uint64_t now) = 0; virtual void addEntityInternal(EntityItemPointer entity) = 0; virtual void removeEntityInternal(EntityItemPointer entity); - virtual void changeEntityInternal(EntityItemPointer entity) = 0; + virtual void processChangedEntity(const EntityItemPointer& entity); virtual void clearEntitiesInternal() = 0; void expireMortalEntities(uint64_t now); @@ -114,11 +117,11 @@ private: // We maintain multiple lists, each for its distinct purpose. // An entity may be in more than one list. + std::unordered_set _changedEntities; // all changes this frame SetOfEntities _allEntities; // tracks all entities added the simulation SetOfEntities _mortalEntities; // entities that have an expiry uint64_t _nextExpiry; - SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update() }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 688f10cdee..fe8c8c9336 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2188,11 +2188,19 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) { _needsParentFixup.append(entity); } +void EntityTree::preUpdate() { + withWriteLock([&] { + fixupNeedsParentFixups(); + if (_simulation) { + _simulation->processChangedEntities(); + } + }); +} + void EntityTree::update(bool simulate) { PROFILE_RANGE(simulation_physics, "UpdateTree"); PerformanceTimer perfTimer("updateTree"); withWriteLock([&] { - fixupNeedsParentFixups(); if (simulate && _simulation) { _simulation->updateEntities(); { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index c80517c82b..9108f8d8d2 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -109,9 +109,10 @@ public: virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override; - virtual void update() override { update(true); } - - void update(bool simulate); + // Why preUpdate() and update()? + // Because sometimes we need to do stuff between the two. + void preUpdate() override; + void update(bool simulate = true) override; // The newer API... void postAddEntity(EntityItemPointer entityItem); diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 28dc7b26c4..b8e3df2d03 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -85,7 +85,9 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { _entitiesThatNeedSimulationOwner.remove(entity); } -void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { +void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) { + EntitySimulation::processChangedEntity(entity); + uint32_t flags = entity->getDirtyFlags(); if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) { if (entity->getSimulatorID().isNull()) { diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 8ac9b69e93..1b240a8bf0 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -31,7 +31,7 @@ protected: void updateEntitiesInternal(uint64_t now) override; void addEntityInternal(EntityItemPointer entity) override; void removeEntityInternal(EntityItemPointer entity) override; - void changeEntityInternal(EntityItemPointer entity) override; + void processChangedEntity(const EntityItemPointer& entity) override; void clearEntitiesInternal() override; void sortEntitiesThatMoved() override; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 82076f618b..4f994da60e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -145,7 +145,10 @@ public: virtual bool rootElementHasData() const { return false; } virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { } - virtual void update() { } // nothing to do by default + // Why preUpdate() and update()? + // Because EntityTree needs them. + virtual void preUpdate() { } + virtual void update(bool simulate = true) { } OctreeElementPointer getRoot() { return _rootElement; } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 20ba3cde60..dd2cc12d50 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -242,6 +242,7 @@ bool OctreePersistThread::backupCurrentFile() { } void OctreePersistThread::process() { + _tree->preUpdate(); _tree->update(); auto now = std::chrono::steady_clock::now(); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 10b5532cd8..daf2ce0298 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -131,10 +131,10 @@ void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntitie _deadAvatarEntities.clear(); } -void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { +void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& entity) { + EntitySimulation::processChangedEntity(entity); + // queue incoming changes: from external sources (script, EntityServer, etc) to physics engine - QMutexLocker lock(&_mutex); - assert(entity); EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); uint8_t region = _space->getRegion(entity->getSpaceIndex()); bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical(); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index d7d8adffac..956674c070 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -72,7 +72,7 @@ protected: // only called by EntitySimulation virtual void updateEntitiesInternal(uint64_t now) override; virtual void addEntityInternal(EntityItemPointer entity) override; virtual void removeEntityInternal(EntityItemPointer entity) override; - virtual void changeEntityInternal(EntityItemPointer entity) override; + void processChangedEntity(const EntityItemPointer& entity) override; virtual void clearEntitiesInternal() override; void removeOwnershipData(EntityMotionState* motionState); From a0841c937cb79823c8ad536efc47f40ddef937be Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 May 2019 17:48:23 -0700 Subject: [PATCH 26/83] fix logic around updating off-thread shapes --- interface/src/avatar/AvatarManager.cpp | 4 +- interface/src/avatar/AvatarMotionState.cpp | 4 +- interface/src/avatar/AvatarMotionState.h | 2 +- interface/src/avatar/DetailedMotionState.cpp | 18 +++++- interface/src/avatar/DetailedMotionState.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 4 +- libraries/physics/src/EntityMotionState.h | 2 +- libraries/physics/src/ObjectMotionState.cpp | 4 +- libraries/physics/src/ObjectMotionState.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 59 ++++++++----------- .../physics/src/PhysicalEntitySimulation.h | 3 - libraries/physics/src/PhysicsEngine.cpp | 2 - 12 files changed, 54 insertions(+), 52 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8537217756..c49db0345f 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -507,13 +507,13 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact } else { uint32_t flags = motionState->getIncomingDirtyFlags(); motionState->clearIncomingDirtyFlags(); - if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { motionState->handleEasyChanges(flags); } // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. - if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { transaction.objectsToReinsert.push_back(motionState); for (auto detailedMotionState : avatar->getDetailedMotionStates()) { transaction.objectsToReinsert.push_back(detailedMotionState); diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index ae229dc66f..97085c8443 100755 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -39,9 +39,9 @@ uint32_t AvatarMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void AvatarMotionState::clearIncomingDirtyFlags() { +void AvatarMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 5f26b5114d..a752d2c8fb 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -28,7 +28,7 @@ public: PhysicsMotionType getMotionType() const override { return _motionType; } uint32_t getIncomingDirtyFlags() const override; - void clearIncomingDirtyFlags() override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; PhysicsMotionType computePhysicsMotionType() const override; diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index 4cfbbf032c..02a2b9d425 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -43,9 +43,9 @@ uint32_t DetailedMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void DetailedMotionState::clearIncomingDirtyFlags() { +void DetailedMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } @@ -157,7 +157,19 @@ void DetailedMotionState::setRigidBody(btRigidBody* body) { } void DetailedMotionState::setShape(const btCollisionShape* shape) { - ObjectMotionState::setShape(shape); + if (_shape != shape) { + if (_shape) { + getShapeManager()->releaseShape(_shape); + } + _shape = shape; + if (_body) { + assert(_shape); + _body->setCollisionShape(const_cast(_shape)); + } + } else if (shape) { + // we need to release unused reference to shape + getShapeManager()->releaseShape(shape); + } } void DetailedMotionState::forceActive() { diff --git a/interface/src/avatar/DetailedMotionState.h b/interface/src/avatar/DetailedMotionState.h index 95b0600cf9..de59f41310 100644 --- a/interface/src/avatar/DetailedMotionState.h +++ b/interface/src/avatar/DetailedMotionState.h @@ -28,7 +28,7 @@ public: PhysicsMotionType getMotionType() const override { return _motionType; } uint32_t getIncomingDirtyFlags() const override; - void clearIncomingDirtyFlags() override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; PhysicsMotionType computePhysicsMotionType() const override; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 24ce17d6fb..69da64b899 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -630,9 +630,9 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() const { return dirtyFlags; } -void EntityMotionState::clearIncomingDirtyFlags() { +void EntityMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body && _entity) { - _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); + _entity->clearDirtyFlags(mask); } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 0d5a97eeb8..7d42c2fc7a 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -56,7 +56,7 @@ public: void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); virtual uint32_t getIncomingDirtyFlags() const override; - virtual void clearIncomingDirtyFlags() override; + virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 92482437e2..ad7332cb15 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -198,7 +198,9 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { getShapeManager()->releaseShape(_shape); } _shape = shape; - if (_body && _type != MOTIONSTATE_TYPE_DETAILED) { + if (_body) { + assert(_shape); + _body->setCollisionShape(const_cast(_shape)); updateCCDConfiguration(); } } else if (shape) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 7d204194b2..415b388e70 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -123,7 +123,7 @@ public: virtual glm::vec3 getObjectLinearVelocityChange() const; virtual uint32_t getIncomingDirtyFlags() const = 0; - virtual void clearIncomingDirtyFlags() = 0; + virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) = 0; virtual PhysicsMotionType computePhysicsMotionType() const = 0; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index daf2ce0298..f1f356cef7 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -237,7 +237,6 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { _incomingChanges.insert(motionState); }; - QMutexLocker lock(&_mutex); uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); if (deliveryCount != _lastWorkDeliveryCount) { // new off-thread shapes have arrived --> find adds whose shapes have arrived @@ -343,41 +342,22 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { } } -void PhysicalEntitySimulation::setObjectsToChange(const VectorOfMotionStates& objectsToChange) { - QMutexLocker lock(&_mutex); - for (auto object : objectsToChange) { - _incomingChanges.insert(static_cast(object)); - } -} - -void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - QMutexLocker lock(&_mutex); - for (auto stateItr : _incomingChanges) { - EntityMotionState* motionState = &(*stateItr); - result.push_back(motionState); - } - _incomingChanges.clear(); -} - void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + QMutexLocker lock(&_mutex); buildMotionStatesForEntitiesThatNeedThem(); for (auto& object : _incomingChanges) { uint32_t flags = object->getIncomingDirtyFlags(); - object->clearIncomingDirtyFlags(); + uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS; bool isInPhysicsSimulation = object->isInPhysicsSimulation(); - if (isInPhysicsSimulation != object->shouldBeInPhysicsSimulation()) { - if (isInPhysicsSimulation) { - transaction.objectsToRemove.push_back(object); - continue; - } else { - transaction.objectsToAdd.push_back(object); - } + bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation(); + if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) { + transaction.objectsToRemove.push_back(object); + continue; } - bool reinsert = false; - if (object->needsNewShape()) { + bool needsNewShape = object->needsNewShape(); + if (needsNewShape) { ShapeType shapeType = object->getShapeType(); if (shapeType == SHAPE_TYPE_STATIC_MESH) { ShapeRequest shapeRequest(object->_entity); @@ -389,13 +369,15 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { object->setShape(shape); - reinsert = true; + handledFlags |= Simulation::DIRTY_SHAPE; + needsNewShape = false; } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait shapeRequest.shapeHash = shapeInfo.getHash(); _shapeRequests.insert(shapeRequest); } else { // failed to build shape --> will not be added/updated + handledFlags |= Simulation::DIRTY_SHAPE; } } else { // continue waiting for shape request @@ -406,24 +388,35 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { object->setShape(shape); - reinsert = true; + handledFlags |= Simulation::DIRTY_SHAPE; + needsNewShape = false; } else { // failed to build shape --> will not be added } } } if (!isInPhysicsSimulation) { - continue; + if (needsNewShape) { + // skip it + continue; + } else { + transaction.objectsToAdd.push_back(object); + handledFlags = DIRTY_PHYSICS_FLAGS; + } } - if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { object->handleEasyChanges(flags); } - if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP) || reinsert) { + if ((flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) || (handledFlags & Simulation::DIRTY_SHAPE)) { transaction.objectsToReinsert.push_back(object); + handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } + object->clearIncomingDirtyFlags(handledFlags); } + _incomingChanges.clear(); } void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 956674c070..817f92cb3c 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -81,9 +81,6 @@ protected: // only called by EntitySimulation public: virtual void prepareEntityForDelete(EntityItemPointer entity) override; - void setObjectsToChange(const VectorOfMotionStates& objectsToChange); - void getObjectsToChange(VectorOfMotionStates& result); - void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index bae9ef2485..2e1a1a401e 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -178,8 +178,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { int32_t group, mask; motionState->computeCollisionGroupAndMask(group, mask); _dynamicsWorld->addRigidBody(body, group, mask); - - motionState->clearIncomingDirtyFlags(); } QList PhysicsEngine::removeDynamicsForBody(btRigidBody* body) { From af1f4364efeb4e9be38b23f73a780fc013104c57 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 May 2019 13:24:25 -0700 Subject: [PATCH 27/83] don't forget to add detailedMotionStates to simulation --- interface/src/avatar/AvatarManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c49db0345f..364ba2d5f0 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -295,7 +295,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { render::Transaction renderTransaction; workload::Transaction workloadTransaction; - + for (int p = kHero; p < NumVariants; p++) { auto& priorityQueue = avatarPriorityQueues[p]; // Sorting the current queue HERE as part of the measured timing. @@ -471,13 +471,17 @@ void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction OtherAvatar::BodyLOD lod = avatar->getBodyLOD(); if (lod == OtherAvatar::BodyLOD::Sphere) { auto dMotionState = createDetailedMotionState(avatar, -1); - detailedMotionStates.push_back(dMotionState); + if (dMotionState) { + detailedMotionStates.push_back(dMotionState); + transaction.objectsToAdd.push_back(dMotionState); + } } else { int32_t numJoints = avatar->getJointCount(); for (int32_t i = 0; i < numJoints; i++) { auto dMotionState = createDetailedMotionState(avatar, i); if (dMotionState) { detailedMotionStates.push_back(dMotionState); + transaction.objectsToAdd.push_back(dMotionState); } } } From 6e27b4d1c5946fd946a394a5dd8e4371349d5fd1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 May 2019 15:40:26 -0700 Subject: [PATCH 28/83] remove bodies of deleted entities --- .../physics/src/PhysicalEntitySimulation.cpp | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index f1f356cef7..4e73b6f966 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -344,9 +344,29 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { QMutexLocker lock(&_mutex); + // entities being removed + for (auto entity : _entitiesToRemoveFromPhysics) { + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + transaction.objectsToRemove.push_back(motionState); + _incomingChanges.remove(motionState); + } + if (_shapeRequests.size() > 0) { + ShapeRequest shapeRequest(entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + _shapeRequests.erase(requestItr); + } + } + } + _entitiesToRemoveFromPhysics.clear(); + + // entities to add buildMotionStatesForEntitiesThatNeedThem(); + + // motionStates with changed entities: delete, add, or change for (auto& object : _incomingChanges) { - uint32_t flags = object->getIncomingDirtyFlags(); + uint32_t unhandledFlags = object->getIncomingDirtyFlags(); uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS; bool isInPhysicsSimulation = object->isInPhysicsSimulation(); @@ -402,16 +422,17 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio } else { transaction.objectsToAdd.push_back(object); handledFlags = DIRTY_PHYSICS_FLAGS; + unhandledFlags = 0; } } - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); + if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(unhandledFlags); } - if ((flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) || (handledFlags & Simulation::DIRTY_SHAPE)) { + if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) { transaction.objectsToReinsert.push_back(object); handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); - } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { + } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } object->clearIncomingDirtyFlags(handledFlags); From e770bd6142013b80f7d801b42a05595ee2512051 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 13:39:51 -0700 Subject: [PATCH 29/83] build CollisionShapes and load RigidBodies even when not yet stepping --- interface/src/Application.cpp | 62 ++++++++++----------- interface/src/octree/SafeLanding.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 44 +++++++-------- libraries/entities/src/EntityItem.h | 3 +- libraries/physics/src/EntityMotionState.cpp | 9 +++ libraries/physics/src/EntityMotionState.h | 2 + libraries/shared/src/SimulationFlags.h | 7 ++- 7 files changed, 71 insertions(+), 58 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1474a954fb..81bfd6127b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6401,40 +6401,40 @@ void Application::update(float deltaTime) { getEntities()->preUpdate(); - if (_physicsEnabled) { - auto t0 = std::chrono::high_resolution_clock::now(); - auto t1 = t0; + auto t0 = std::chrono::high_resolution_clock::now(); + auto t1 = t0; + { + PROFILE_RANGE(simulation_physics, "PrePhysics"); + PerformanceTimer perfTimer("prePhysics)"); { - PROFILE_RANGE(simulation_physics, "PrePhysics"); - PerformanceTimer perfTimer("prePhysics)"); - { - PROFILE_RANGE(simulation_physics, "Entities"); - PhysicsEngine::Transaction transaction; - _entitySimulation->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - _entitySimulation->handleProcessedPhysicsTransaction(transaction); - } + PROFILE_RANGE(simulation_physics, "Entities"); + PhysicsEngine::Transaction transaction; + _entitySimulation->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + _entitySimulation->handleProcessedPhysicsTransaction(transaction); + } + t1 = std::chrono::high_resolution_clock::now(); + + { + PROFILE_RANGE(simulation_physics, "Avatars"); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + + myAvatar->prepareForPhysicsSimulation(); + _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + } + } + + if (_physicsEnabled) { + { + PROFILE_RANGE(simulation_physics, "PrepareActions"); _entitySimulation->applyDynamicChanges(); - - t1 = std::chrono::high_resolution_clock::now(); - - { - PROFILE_RANGE(simulation_physics, "Avatars"); - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - myAvatar->prepareForPhysicsSimulation(); - _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); - } - - { - PROFILE_RANGE(simulation_physics, "PrepareActions"); - _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { - dynamic->prepareForPhysicsSimulation(); - }); - } + _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { + dynamic->prepareForPhysicsSimulation(); + }); } auto t2 = std::chrono::high_resolution_clock::now(); { diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index e56ca984e0..479c2a5860 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -168,7 +168,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool hasAABox; entity->getAABox(hasAABox); if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { - return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad()); + return (!entity->shouldBePhysical() || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad()); } } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7cc35f8be0..d91c11e726 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1828,42 +1828,42 @@ void EntityItem::setParentID(const QUuid& value) { if (!value.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(value); if (entity) { - newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } } if (!oldParentID.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID); if (entity) { - oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } } if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) { - newParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + newParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) { - oldParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + oldParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { - if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { + markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } else { - clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } @@ -2102,7 +2102,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask } } - if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { + if ((bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask); @@ -2173,8 +2173,8 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin } void EntityItem::enableNoBootstrap() { - if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + if (!(bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { + _flags |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here @@ -2186,7 +2186,7 @@ void EntityItem::enableNoBootstrap() { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } @@ -2194,7 +2194,7 @@ void EntityItem::enableNoBootstrap() { void EntityItem::disableNoBootstrap() { if (!stillHasMyGrabAction()) { - _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags &= ~Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar EntityTreePointer entityTree = getTree(); @@ -2207,7 +2207,7 @@ void EntityItem::disableNoBootstrap() { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); simulation->changeEntity(entity); } }); @@ -2326,7 +2326,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) { disableNoBootstrap(); } else { - // NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct + // NO-OP: we assume SPECIAL_FLAG_NO_BOOTSTRAPPING bits and collision group are correct // because they should have been set correctly when the action was added // and/or when children were linked } @@ -3154,21 +3154,21 @@ DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVer uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { - result = _flags & Simulation::DIRTY_FLAGS; + result = _flags & Simulation::DIRTY_FLAGS_MASK; }); return result; } void EntityItem::markDirtyFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::DIRTY_FLAGS; + mask &= Simulation::DIRTY_FLAGS_MASK; _flags |= mask; }); } void EntityItem::clearDirtyFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::DIRTY_FLAGS; + mask &= Simulation::DIRTY_FLAGS_MASK; _flags &= ~mask; }); } @@ -3176,21 +3176,21 @@ void EntityItem::clearDirtyFlags(uint32_t mask) { uint32_t EntityItem::getSpecialFlags() const { uint32_t result; withReadLock([&] { - result = _flags & Simulation::SPECIAL_FLAGS; + result = _flags & Simulation::SPECIAL_FLAGS_MASK; }); return result; } void EntityItem::markSpecialFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::SPECIAL_FLAGS; + mask &= Simulation::SPECIAL_FLAGS_MASK; _flags |= mask; }); } void EntityItem::clearSpecialFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::SPECIAL_FLAGS; + mask &= Simulation::SPECIAL_FLAGS_MASK; _flags &= ~mask; }); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b99536f4dc..ea4b11e0b0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -424,8 +424,9 @@ public: bool isSimulated() const { return _simulated; } - void* getPhysicsInfo() const { return _physicsInfo; } + bool isInPhysicsSimulation() const { return (bool)(_flags & Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); } + void* getPhysicsInfo() const { return _physicsInfo; } void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 69da64b899..67aa7d2d7d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -743,6 +743,15 @@ bool EntityMotionState::shouldSendBid() const { && !_entity->getLocked(); } +void EntityMotionState::setRigidBody(btRigidBody* body) { + ObjectMotionState::setRigidBody(body); + if (_body) { + _entity->markSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); + } else { + _entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); + } +} + uint8_t EntityMotionState::computeFinalBidPriority() const { return (_region == workload::Region::R1) ? glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0; diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 7d42c2fc7a..7456837777 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -100,6 +100,8 @@ public: void saveKinematicState(btScalar timeStep) override; protected: + void setRigidBody(btRigidBody* body) override; + uint8_t computeFinalBidPriority() const; void updateSendVelocities(); uint64_t getNextBidExpiry() const { return _nextBidExpiry; } diff --git a/libraries/shared/src/SimulationFlags.h b/libraries/shared/src/SimulationFlags.h index af8c0e5fce..9dbb719af2 100644 --- a/libraries/shared/src/SimulationFlags.h +++ b/libraries/shared/src/SimulationFlags.h @@ -40,13 +40,14 @@ namespace Simulation { const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed // bits 17-32 are reservied for special flags - const uint32_t SPECIAL_FLAGS_NO_BOOTSTRAPPING = 0x10000; + const uint32_t SPECIAL_FLAG_NO_BOOTSTRAPPING = 0x10000; + const uint32_t SPECIAL_FLAG_IN_PHYSICS_SIMULATION = 0x20000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; - const uint32_t SPECIAL_FLAGS = SPECIAL_FLAGS_NO_BOOTSTRAPPING; + const uint32_t SPECIAL_FLAGS_MASK = SPECIAL_FLAG_NO_BOOTSTRAPPING | SPECIAL_FLAG_IN_PHYSICS_SIMULATION; - const uint32_t DIRTY_FLAGS = DIRTY_POSITION | + const uint32_t DIRTY_FLAGS_MASK = DIRTY_POSITION | DIRTY_ROTATION | DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY | From f6916edac458ec6fa507685a8b1d526deac1e6a0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 14:48:10 -0700 Subject: [PATCH 30/83] cleanup and removing some accidental C++-20-isms --- interface/src/avatar/AvatarManager.cpp | 4 +--- libraries/entities/src/EntitySimulation.cpp | 16 ---------------- libraries/physics/src/ShapeManager.h | 4 ++-- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 364ba2d5f0..ac74d0ab20 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -427,8 +427,6 @@ DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer std::vector boundJoints; const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints); if (shape) { - //std::shared_ptr avatar = shared_from_this(); - //std::shared_ptr avatar = getThisPointer(); DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok motionState->setIsBound(isBound, boundJoints); @@ -514,7 +512,7 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact if (flags & EASY_DIRTY_PHYSICS_FLAGS) { motionState->handleEasyChanges(flags); } - // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC + // NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index e0a6d7af7c..b5e4fed0fd 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -192,22 +192,6 @@ void EntitySimulation::processChangedEntities() { void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) { uint32_t dirtyFlags = entity->getDirtyFlags(); - /* TODO? maybe add to _entitiesToSort when DIRTY_POSITION is set? - // Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes - // it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence - // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag. - if (dirtyFlags & Simulation::DIRTY_POSITION) { - AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); - bool success; - AACube newCube = entity->getQueryAACube(success); - if (success && !domainBounds.touches(newCube)) { - qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; - entity->die(); - prepareEntityForDelete(entity); - return; - } - } - */ if (dirtyFlags & (Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE)) { if (dirtyFlags & Simulation::DIRTY_LIFETIME) { diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index b07999ec64..d895ad204e 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -104,8 +104,8 @@ private: ShapeFactory::Worker* _deadWorker { nullptr }; TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; - std::atomic_uint32_t _workRequestCount { 0 }; - std::atomic_uint32_t _workDeliveryCount { 0 }; + std::atomic_uint _workRequestCount { 0 }; + std::atomic_uint _workDeliveryCount { 0 }; }; #endif // hifi_ShapeManager_h From 53ad41e5eea089269b021b01418ab635ff43c0b5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 16:55:40 -0700 Subject: [PATCH 31/83] include in ShapeManager.h --- libraries/physics/src/ShapeManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index d895ad204e..1f39fd56be 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -13,6 +13,7 @@ #define hifi_ShapeManager_h #include +#include #include #include From d6a20a4abd4e7b0d001b0d474372fe5936fcc691 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 17:00:40 -0700 Subject: [PATCH 32/83] fix inverted logic typo --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 4e73b6f966..9672d53f92 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -354,7 +354,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio if (_shapeRequests.size() > 0) { ShapeRequest shapeRequest(entity); ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); - if (requestItr == _shapeRequests.end()) { + if (requestItr != _shapeRequests.end()) { _shapeRequests.erase(requestItr); } } From bef053584b9a30a44b9447d3a2a9b151e6a38300 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 14:45:56 -0700 Subject: [PATCH 33/83] don't forget to clear _activeStaticBodies --- libraries/physics/src/PhysicsEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 2e1a1a401e..976c547c5e 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -250,6 +250,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { } object->clearIncomingDirtyFlags(); } + _activeStaticBodies.clear(); } void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) { From 81f960a4bdb7bc7b95c6e84c2837ea34f05c676c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 14:47:00 -0700 Subject: [PATCH 34/83] remove from _physicalObjects list after transaction --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 9672d53f92..08ea98eef1 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -156,7 +156,6 @@ void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& ent // remove from the physical simulation _incomingChanges.remove(motionState); - _physicalObjects.remove(motionState); removeOwnershipData(motionState); _entitiesToRemoveFromPhysics.insert(entity); if (canBeKinematic && entity->isMovingRelativeToParent()) { @@ -443,6 +442,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { // things on objectsToRemove are ready for delete for (auto object : transaction.objectsToRemove) { + _physicalObjects.remove(object); delete object; } transaction.clear(); From 8e6394c95f10b7dcdfd7fed5e5bc6809788bdc73 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 17:34:51 -0700 Subject: [PATCH 35/83] ParticleEffectEntityItem should not be in physics simulation --- libraries/entities/src/ParticleEffectEntityItem.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 52f229201e..b526298a4b 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -231,6 +231,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + bool shouldBePhysical() const override { return false; } + void setColor(const glm::u8vec3& value); glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; } From a28d185afc7a5322fe352b03caf0bf79168a1ae6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 10:39:44 -0700 Subject: [PATCH 36/83] make explicit: zones shouldn't be in physics simulation --- libraries/entities/src/ZoneEntityItem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 69e3227135..34ad47f095 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -62,6 +62,7 @@ public: virtual bool isReadyToComputeShape() const override { return false; } virtual void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; + bool shouldBePhysical() const override { return false; } QString getCompoundShapeURL() const; virtual void setCompoundShapeURL(const QString& url); From 71f7132a6b1d57d4b179ccf3ce73be3429a550d0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 8 May 2019 09:17:11 +1200 Subject: [PATCH 37/83] Scene JSDoc --- .../src/SceneScriptingInterface.h | 86 ++++++++++++------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index a78fa44641..fa67666676 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -21,10 +21,11 @@ namespace SceneScripting { /**jsdoc - * @typedef {object} Scene.Stage.Location - * @property {number} longitude - * @property {number} latitude - * @property {number} altitude + * Stage location. + * @typedef {object} Stage.Location + * @property {number} longitude - Longitude. + * @property {number} latitude - Latitude. + * @property {number} altitude - Altitude. */ class Location : public QObject { Q_OBJECT @@ -49,9 +50,10 @@ namespace SceneScripting { using LocationPointer = std::unique_ptr; /**jsdoc - * @typedef {object} Scene.Stage.Time - * @property {number} hour - * @property {number} day + * Stage time. + * @typedef {object} Stage.Time + * @property {number} hour - Hour. + * @property {number} day - Day. */ class Time : public QObject { Q_OBJECT @@ -73,11 +75,12 @@ namespace SceneScripting { using TimePointer = std::unique_ptr + * Scene.shouldRenderAvatarsChanged.connect(function (shouldRenderAvatars) { + * print("Should render avatars changed to: " + shouldRenderAvatars); + * }); */ void shouldRenderAvatarsChanged(bool shouldRenderAvatars); /**jsdoc + * Triggered when whether or not entities are rendered changes. * @function Scene.shouldRenderEntitiesChanged - * @param {boolean} shouldRenderEntities + * @param {boolean} shouldRenderEntities - true if entities (domain, avatar, and local) are rendered, + * false if they aren't. * @returns {Signal} */ void shouldRenderEntitiesChanged(bool shouldRenderEntities); From 883b83ec59a743191527044c725157bdc23f681a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 15:15:12 -0700 Subject: [PATCH 38/83] minor name change for more clarity --- libraries/workload/src/workload/View.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/workload/src/workload/View.cpp b/libraries/workload/src/workload/View.cpp index 8cb593d2a7..a11b1890fd 100644 --- a/libraries/workload/src/workload/View.cpp +++ b/libraries/workload/src/workload/View.cpp @@ -37,8 +37,8 @@ View View::evalFromFrustum(const ViewFrustum& frustum, const glm::vec3& offset) Sphere View::evalRegionSphere(const View& view, float originRadius, float maxDistance) { float radius = (maxDistance + originRadius) / 2.0f; - float center = radius - originRadius; - return Sphere(view.origin + view.direction * center, radius); + float distanceToCenter = radius - originRadius; + return Sphere(view.origin + view.direction * distanceToCenter, radius); } void View::updateRegionsDefault(View& view) { From 72c6cad581acabb44ea3c4d7f685ec2163230edc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 15:15:53 -0700 Subject: [PATCH 39/83] cleanup _deadWorker in dtor, more correct _nextOrphanExpiry --- libraries/physics/src/ShapeManager.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 85587a6c67..c37f95b5f1 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -30,6 +30,10 @@ ShapeManager::~ShapeManager() { ShapeFactory::deleteShape(shapeRef->shape); } _shapeMap.clear(); + if (_deadWorker) { + delete _deadWorker; + _deadWorker = nullptr; + } } const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { @@ -230,12 +234,14 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { // refCount on expiry we will move it to _garbageRing. const int64_t SHAPE_EXPIRY = USECS_PER_SECOND; auto now = std::chrono::steady_clock::now(); - auto expiry = now + std::chrono::microseconds(SHAPE_EXPIRY); + auto newExpiry = now + std::chrono::microseconds(SHAPE_EXPIRY); if (_nextOrphanExpiry < now) { + _nextOrphanExpiry = newExpiry; // check for expired orphan shapes size_t i = 0; while (i < _orphans.size()) { - if (_orphans[i].expiry < now) { + auto expiry = _orphans[i].expiry; + if (expiry < now) { uint64_t key = _orphans[i].key; HashKey hashKey(key); ShapeReference* shapeRef = _shapeMap.find(hashKey); @@ -248,12 +254,14 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { _orphans[i] = _orphans.back(); _orphans.pop_back(); } else { + if (expiry < _nextOrphanExpiry) { + _nextOrphanExpiry = expiry; + } ++i; } } } - _nextOrphanExpiry = expiry; - _orphans.push_back(KeyExpiry(newRef.key, expiry)); + _orphans.push_back(KeyExpiry(newRef.key, newExpiry)); } } disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); From 410679b9b7adc17721b381439cc662c517f065a4 Mon Sep 17 00:00:00 2001 From: Saracen Date: Mon, 6 May 2019 00:30:32 +0100 Subject: [PATCH 40/83] Render lightmaps in the forward renderer --- .../render-utils/src/RenderPipelines.cpp | 4 +- .../src/forward_model_lightmap.slf | 71 ++++++++++++++++++ .../src/forward_model_normal_map_lightmap.slf | 74 +++++++++++++++++++ .../render-utils/forward_model_lightmap.slp | 1 + .../forward_model_normal_map_lightmap.slp | 1 + 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 libraries/render-utils/src/forward_model_lightmap.slf create mode 100644 libraries/render-utils/src/forward_model_normal_map_lightmap.slf create mode 100644 libraries/render-utils/src/render-utils/forward_model_lightmap.slp create mode 100644 libraries/render-utils/src/render-utils/forward_model_normal_map_lightmap.slp diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index ac2eb8e475..2817abb4a1 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -228,9 +228,11 @@ void initForwardPipelines(ShapePlumber& plumber) { // Opaques addPipeline(Key::Builder().withMaterial(), program::forward_model); + addPipeline(Key::Builder().withMaterial().withLightmap(), program::forward_model_lightmap); addPipeline(Key::Builder().withMaterial().withUnlit(), program::forward_model_unlit); addPipeline(Key::Builder().withMaterial().withTangents(), program::forward_model_normal_map); - + addPipeline(Key::Builder().withMaterial().withTangents().withLightmap(), program::forward_model_normal_map_lightmap); + // Deformed Opaques addPipeline(Key::Builder().withMaterial().withDeformed(), program::forward_deformed_model); addPipeline(Key::Builder().withMaterial().withDeformed().withTangents(), program::forward_deformed_model_normal_map); diff --git a/libraries/render-utils/src/forward_model_lightmap.slf b/libraries/render-utils/src/forward_model_lightmap.slf new file mode 100644 index 0000000000..aa1d6dc3b8 --- /dev/null +++ b/libraries/render-utils/src/forward_model_lightmap.slf @@ -0,0 +1,71 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gateau on 2/15/2016. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DefaultMaterials.slh@> +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + +<@include ForwardGlobalLight.slh@> + +<$declareEvalLightmappedColor()$> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> +<$declareMaterialLightmap()$> + +layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; +#define _texCoord0 _texCoord01.xy +#define _texCoord1 _texCoord01.zw +layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; +layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + + vec3 fragNormal = normalize(_normalWS); + + TransformCamera cam = getTransformCamera(); + + vec4 color = vec4(evalLightmappedColor( + cam._viewInverse, + 1.0, + 1.0, + fragNormal, + albedo, + lightmap), + opacity); + + _fragColor0 = color; +} diff --git a/libraries/render-utils/src/forward_model_normal_map_lightmap.slf b/libraries/render-utils/src/forward_model_normal_map_lightmap.slf new file mode 100644 index 0000000000..c36f3d51c6 --- /dev/null +++ b/libraries/render-utils/src/forward_model_normal_map_lightmap.slf @@ -0,0 +1,74 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gateau on 2/15/2016. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DefaultMaterials.slh@> +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + +<@include ForwardGlobalLight.slh@> + +<$declareEvalLightmappedColor()$> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> +<$declareMaterialLightmap()$> + +layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; +#define _texCoord0 _texCoord01.xy +#define _texCoord1 _texCoord01.zw +layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; +layout(location=RENDER_UTILS_ATTR_TANGENT_WS) in vec3 _tangentWS; +layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + + vec3 fragPosition = _positionES.xyz; + vec3 fragNormal; + <$evalMaterialNormalLOD(fragPosition, normalTex, _normalWS, _tangentWS, fragNormal)$> + + TransformCamera cam = getTransformCamera(); + + vec4 color = vec4(evalLightmappedColor( + cam._viewInverse, + 1.0, + 1.0, + fragNormal, + albedo, + lightmap), + opacity); + + _fragColor0 = color; +} diff --git a/libraries/render-utils/src/render-utils/forward_model_lightmap.slp b/libraries/render-utils/src/render-utils/forward_model_lightmap.slp new file mode 100644 index 0000000000..81ac672062 --- /dev/null +++ b/libraries/render-utils/src/render-utils/forward_model_lightmap.slp @@ -0,0 +1 @@ +VERTEX model diff --git a/libraries/render-utils/src/render-utils/forward_model_normal_map_lightmap.slp b/libraries/render-utils/src/render-utils/forward_model_normal_map_lightmap.slp new file mode 100644 index 0000000000..c50be6285b --- /dev/null +++ b/libraries/render-utils/src/render-utils/forward_model_normal_map_lightmap.slp @@ -0,0 +1 @@ +VERTEX model_normal_map From da092cc5f0bdbb7edfae21c995639eb949a261af Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 8 May 2019 11:04:54 -0700 Subject: [PATCH 41/83] fix zone issues --- .../src/EntityTreeRenderer.cpp | 224 ++++++++---------- .../src/EntityTreeRenderer.h | 44 ++-- .../src/RenderableZoneEntityItem.cpp | 70 +----- .../src/RenderableZoneEntityItem.h | 34 +-- libraries/render-utils/src/ZoneRenderer.cpp | 2 +- libraries/render/src/render/Scene.cpp | 21 +- libraries/render/src/render/Scene.h | 3 - libraries/render/src/render/Selection.h | 3 +- 8 files changed, 138 insertions(+), 263 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7d130125b3..61aa3b5d2d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -220,6 +220,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { stopDomainAndNonOwnedEntities(); + auto sessionUUID = getTree()->getMyAvatarSessionUUID(); std::unordered_map savedEntities; // remove all entities from the scene auto scene = _viewState->getMain3DScene(); @@ -227,7 +228,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const EntityItemPointer& entityItem = renderer->getEntity(); - if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == sessionUUID))) { fadeOutRenderable(renderer); } else { savedEntities[entry.first] = entry.second; @@ -238,7 +239,9 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { _renderablesToUpdate = savedEntities; _entitiesInScene = savedEntities; - _layeredZones.clearNonLocalLayeredZones(); + if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) { + applyLayeredZones(); + } OctreeProcessor::clearDomainAndNonOwnedEntities(); } @@ -271,6 +274,9 @@ void EntityTreeRenderer::clear() { // reset the zone to the default (while we load the next scene) _layeredZones.clear(); + if (!_shuttingDown) { + applyLayeredZones(); + } OctreeProcessor::clear(); } @@ -363,6 +369,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r for (const auto& processedId : processedIds) { _entitiesToAdd.erase(processedId); } + forceRecheckEntities(); } } } @@ -537,8 +544,7 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair proxyUp _spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second); } -bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar) { - bool didUpdate = false; +void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar) { float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later QVector entityIDs; @@ -550,7 +556,7 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSetevalEntitiesInSphere(_avatarPosition, radius, PickFilter(), entityIDs); - LayeredZones oldLayeredZones(std::move(_layeredZones)); + LayeredZones oldLayeredZones(_layeredZones); _layeredZones.clear(); // create a list of entities that actually contain the avatar's position @@ -578,8 +584,8 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSetgetVisible() && renderableForEntity(entity)) { - _layeredZones.insert(std::dynamic_pointer_cast(entity)); + if (isZone && entity->getVisible() && renderableIdForEntity(entity) != render::Item::INVALID_ITEM_ID) { + _layeredZones.emplace(std::dynamic_pointer_cast(entity)); } if ((!hasScript && isZone) || scriptHasLoaded) { @@ -588,24 +594,16 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSetgetAvatarPosition(); @@ -623,7 +621,7 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { _forceRecheckEntities = false; QSet entitiesContainingAvatar; - didUpdate = findBestZoneAndMaybeContainingEntities(entitiesContainingAvatar); + findBestZoneAndMaybeContainingEntities(entitiesContainingAvatar); // Note: at this point we don't need to worry about the tree being locked, because we only deal with // EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts @@ -649,7 +647,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { } } } - return didUpdate; } void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { @@ -696,18 +693,12 @@ bool EntityTreeRenderer::applyLayeredZones() { // in the expected layered order and update the scene with it auto scene = _viewState->getMain3DScene(); if (scene) { - render::Transaction transaction; render::ItemIDs list; - for (auto& zone : _layeredZones) { - auto id = renderableIdForEntity(zone.zone); - // The zone may not have been rendered yet. - if (id != render::Item::INVALID_ITEM_ID) { - list.push_back(id); - } - } - render::Selection selection("RankedZones", list); - transaction.resetSelection(selection); + _layeredZones.appendRenderIDs(list, this); + render::Selection selection("RankedZones", list); + render::Transaction transaction; + transaction.resetSelection(selection); scene->enqueueTransaction(transaction); } else { qCWarning(entitiesrenderer) << "EntityTreeRenderer::applyLayeredZones(), Unexpected null scene, possibly during application shutdown"; @@ -1018,7 +1009,6 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { } void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { - forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities checkAndCallPreload(entityID); auto entity = std::static_pointer_cast(_tree)->findEntityByID(entityID); if (entity) { @@ -1190,107 +1180,96 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) { } void EntityTreeRenderer::updateZone(const EntityItemID& id) { - // Get in the zone! - auto zone = std::dynamic_pointer_cast(getTree()->findEntityByEntityItemID(id)); - if (zone && zone->contains(_avatarPosition)) { - _layeredZones.update(zone); + if (auto zone = std::dynamic_pointer_cast(getTree()->findEntityByEntityItemID(id))) { + _layeredZones.update(zone, _avatarPosition, this); + applyLayeredZones(); } } -EntityTreeRenderer::LayeredZones::LayeredZones(LayeredZones&& other) { - // In a swap: - // > All iterators and references remain valid. The past-the-end iterator is invalidated. - bool isSkyboxLayerValid = (other._skyboxLayer != other.end()); +bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& sessionUUID) { + bool zonesChanged = false; - swap(other); - _map.swap(other._map); - _skyboxLayer = other._skyboxLayer; + auto it = c.begin(); + while (it != c.end()) { + if (!(it->zone->isLocalEntity() || (it->zone->isAvatarEntity() && it->zone->getOwningAvatarID() == sessionUUID))) { + zonesChanged = true; + it = c.erase(it); + } else { + it++; + } + } - if (!isSkyboxLayerValid) { - _skyboxLayer = end(); + if (zonesChanged) { + std::make_heap(c.begin(), c.end(), comp); + } + return zonesChanged; +} + +std::pair EntityTreeRenderer::LayeredZones::getZoneInteractionProperties() const { + auto it = c.cbegin(); + while (it != c.cend()) { + if (it->zone && it->zone->isDomainEntity()) { + return { it->zone->getFlyingAllowed(), it->zone->getGhostingAllowed() }; + } + it++; + } + return { true, true }; +} + +void EntityTreeRenderer::LayeredZones::remove(const std::shared_ptr& zone) { + auto it = c.begin(); + while (it != c.end()) { + if (it->zone == zone) { + break; + } + it++; + } + if (it != c.end()) { + c.erase(it); + std::make_heap(c.begin(), c.end(), comp); } } -void EntityTreeRenderer::LayeredZones::clearNonLocalLayeredZones() { - std::set localLayeredZones; - std::map newMap; +void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) { + // When a zone's position or visibility changes, we call this method + // In order to resort our zones, we first remove the changed zone, and then re-insert it if necessary + remove(zone); - for (auto iter = begin(); iter != end(); iter++) { - LayeredZone layeredZone = *iter; + // Only call contains if the zone is rendering + if (zone->isVisible() && entityTreeRenderer->renderableIdForEntity(zone) != render::Item::INVALID_ITEM_ID && zone->contains(position)) { + emplace(zone); + } +} - if (layeredZone.zone->isLocalEntity()) { - bool success; - iterator it; - std::tie(it, success) = localLayeredZones.insert(layeredZone); +bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const { + if (size() != other.size()) { + return false; + } - if (success) { - newMap.emplace(layeredZone.id, it); + auto it = c.cbegin(); + auto otherIt = other.c.cbegin(); + while (it != c.cend()) { + if (*it != *otherIt) { + return false; + } + it++; + otherIt++; + } + + return true; +} + +void EntityTreeRenderer::LayeredZones::appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const { + auto it = c.cbegin(); + while (it != c.cend()) { + if (it->zone) { + auto id = entityTreeRenderer->renderableIdForEntityId(it->id); + if (id != render::Item::INVALID_ITEM_ID) { + list.push_back(id); } } + it++; } - - std::set::operator=(localLayeredZones); - _map = newMap; - _skyboxLayer = empty() ? end() : begin(); -} - -void EntityTreeRenderer::LayeredZones::clear() { - std::set::clear(); - _map.clear(); - _skyboxLayer = end(); -} - -std::pair EntityTreeRenderer::LayeredZones::insert(const LayeredZone& layer) { - iterator it; - bool success; - std::tie(it, success) = std::set::insert(layer); - - if (success) { - _map.emplace(it->id, it); - } - - return { it, success }; -} - -void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone) { - bool isVisible = zone->isVisible(); - - if (empty() && isVisible) { - // there are no zones: set this one - insert(zone); - return; - } else { - LayeredZone zoneLayer(zone); - - // find this zone's layer, if it exists - iterator layer = end(); - auto it = _map.find(zoneLayer.id); - if (it != _map.end()) { - layer = it->second; - // if the volume changed, we need to resort the layer (reinsertion) - // if the visibility changed, we need to erase the layer - if (zoneLayer.volume != layer->volume || !isVisible) { - erase(layer); - _map.erase(it); - layer = end(); - } - } - - // (re)insert this zone's layer if necessary - if (layer == end() && isVisible) { - std::tie(layer, std::ignore) = insert(zoneLayer); - _map.emplace(layer->id, layer); - } - } -} - -bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) { - bool result = std::equal(other.begin(), other._skyboxLayer, begin()); - if (result) { - // if valid, set the _skyboxLayer from the other LayeredZones - _skyboxLayer = std::next(begin(), std::distance(other.begin(), other._skyboxLayer)); - } - return result; } CalculateEntityLoadingPriority EntityTreeRenderer::_calculateEntityLoadingPriorityFunc = [](const EntityItem& item) -> float { @@ -1298,14 +1277,7 @@ CalculateEntityLoadingPriority EntityTreeRenderer::_calculateEntityLoadingPriori }; std::pair EntityTreeRenderer::getZoneInteractionProperties() { - for (auto& zone : _layeredZones) { - // Only domain entities control flying allowed and ghosting allowed - if (zone.zone && zone.zone->isDomainEntity()) { - return { zone.zone->getFlyingAllowed(), zone.zone->getGhostingAllowed() }; - } - } - - return { true, true }; + return _layeredZones.getZoneInteractionProperties(); } bool EntityTreeRenderer::wantsKeyboardFocus(const EntityItemID& id) const { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 05105a2c7f..f7f5ae9f22 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -169,7 +169,7 @@ private: void resetEntitiesScriptEngine(); - bool findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar); + void findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar); bool applyLayeredZones(); void stopDomainAndNonOwnedEntities(); @@ -180,7 +180,7 @@ private: EntityItemID _currentClickingOnEntityID; QScriptValueList createEntityArgs(const EntityItemID& entityID); - bool checkEnterLeaveEntities(); + void checkEnterLeaveEntities(); void leaveDomainAndNonOwnedEntities(); void leaveAllEntities(); void forceRecheckEntities(); @@ -210,48 +210,38 @@ private: class LayeredZone { public: - LayeredZone(std::shared_ptr zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {} - LayeredZone(std::shared_ptr zone) : LayeredZone(zone, zone->getID(), zone->getVolumeEstimate()) {} + LayeredZone(std::shared_ptr zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {} - bool operator<(const LayeredZone& r) const { return std::tie(volume, id) < std::tie(r.volume, r.id); } - bool operator==(const LayeredZone& r) const { return id == r.id; } - bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); } + bool operator>(const LayeredZone& r) const { return volume > r.volume; } + bool operator==(const LayeredZone& r) const { return zone.get() == r.zone.get(); } + bool operator!=(const LayeredZone& r) const { return !(*this == r); } + bool operator>=(const LayeredZone& r) const { return (*this > r) || (*this == r); } std::shared_ptr zone; QUuid id; float volume; }; - class LayeredZones : public std::set { + class LayeredZones : public std::priority_queue, std::greater> { public: - LayeredZones() {}; - LayeredZones(LayeredZones&& other); + void clear() { *this = LayeredZones(); } + bool clearDomainAndNonOwnedZones(const QUuid& sessionUUID); - // avoid accidental misconstruction - LayeredZones(const LayeredZones&) = delete; - LayeredZones& operator=(const LayeredZones&) = delete; - LayeredZones& operator=(LayeredZones&&) = delete; + bool equals(const LayeredZones& other) const; + void remove(const std::shared_ptr& zone); + void update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer); - void clear(); - void clearNonLocalLayeredZones(); - std::pair insert(const LayeredZone& layer); - void update(std::shared_ptr zone); - bool contains(const LayeredZones& other); - - std::shared_ptr getZone() { return empty() ? nullptr : begin()->zone; } - - private: - std::map _map; - iterator _skyboxLayer { end() }; + void appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const; + std::pair getZoneInteractionProperties() const; }; LayeredZones _layeredZones; - float _avgRenderableUpdateCost { 0.0f }; - uint64_t _lastZoneCheck { 0 }; const uint64_t ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const float ZONE_CHECK_DISTANCE = 0.001f; + float _avgRenderableUpdateCost { 0.0f }; + ReadWriteLockable _changedEntitiesGuard; std::unordered_set _changedEntities; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 64cca404cb..e4300cca76 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -92,9 +92,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } { // Sun - // Need an update ? if (_needSunUpdate) { - // Do we need to allocate the light in the stage ? if (LightStage::isIndexInvalid(_sunIndex)) { _sunIndex = _stage->addLight(_sunLight); } else { @@ -107,9 +105,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { { // Ambient updateAmbientMap(); - // Need an update ? if (_needAmbientUpdate) { - // Do we need to allocate the light in the stage ? if (LightStage::isIndexInvalid(_ambientIndex)) { _ambientIndex = _stage->addLight(_ambientLight); } else { @@ -123,7 +119,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { updateSkyboxMap(); if (_needBackgroundUpdate) { - if (_skyboxMode == COMPONENT_MODE_ENABLED && BackgroundStage::isIndexInvalid(_backgroundIndex)) { + if (BackgroundStage::isIndexInvalid(_backgroundIndex)) { _backgroundIndex = _backgroundStage->addBackground(_background); } _needBackgroundUpdate = false; @@ -186,24 +182,19 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } -void ZoneEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& transaction) { -#if 0 - if (_model) { - _model->removeFromScene(scene, transaction); - } -#endif - Parent::removeFromScene(scene, transaction); -} - void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { - DependencyManager::get()->updateZone(entity->getID()); - auto position = entity->getWorldPosition(); auto rotation = entity->getWorldOrientation(); auto dimensions = entity->getScaledDimensions(); bool rotationChanged = rotation != _lastRotation; bool transformChanged = rotationChanged || position != _lastPosition || dimensions != _lastDimensions; + auto visible = entity->getVisible(); + if (transformChanged || visible != _lastVisible) { + _lastVisible = visible; + DependencyManager::get()->updateZone(entity->getID()); + } + auto proceduralUserData = entity->getUserData(); bool proceduralUserDataChanged = _proceduralUserData != proceduralUserData; @@ -226,25 +217,6 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _proceduralUserData = entity->getUserData(); } -#if 0 - if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { - _lastShapeURL = _typedEntity->getCompoundShapeURL(); - _model.reset(); - _model = std::make_shared(); - _model->setIsWireframe(true); - _model->init(); - _model->setURL(_lastShapeURL); - } - - if (_model && _model->isActive()) { - _model->setScaleToFit(true, _lastDimensions); - _model->setSnapModelToRegistrationPoint(true, _entity->getRegistrationPoint()); - _model->setRotation(_lastRotation); - _model->setTranslation(_lastPosition); - _model->simulate(0.0f); - } -#endif - updateKeyZoneItemFromEntity(entity); if (keyLightChanged) { @@ -296,6 +268,10 @@ ItemKey ZoneEntityRenderer::getKey() { } bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { + if (entity->getVisible() != _lastVisible) { + return true; + } + if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || @@ -323,25 +299,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return true; } -#if 0 - if (_typedEntity->getCompoundShapeURL() != _lastShapeURL) { - return true; - } - - if (_model) { - if (!_model->needsFixupInScene() && (!ZoneEntityItem::getDrawZoneBoundaries() || _entity->getShapeType() != SHAPE_TYPE_COMPOUND)) { - return true; - } - - if (_model->needsFixupInScene() && (ZoneEntityItem::getDrawZoneBoundaries() || _entity->getShapeType() == SHAPE_TYPE_COMPOUND)) { - return true; - } - - if (_lastModelActive != _model->isActive()) { - return true; - } - } -#endif + // FIXME: do we need to trigger an update when shapeType changes? see doRenderUpdateAsynchronousTyped return false; } @@ -450,7 +408,6 @@ void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& e } void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { - // nothing change if nothing change if (_ambientTextureURL == ambientUrl) { return; } @@ -466,8 +423,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { _pendingAmbientTexture = true; auto textureCache = DependencyManager::get(); _ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::AMBIENT_TEXTURE); - - // keep whatever is assigned on the ambient map/sphere until texture is loaded } } @@ -492,7 +447,6 @@ void ZoneEntityRenderer::updateAmbientMap() { } void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) { - // nothing change if nothing change if (_skyboxTextureURL == skyboxUrl) { return; } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 2fa55f1540..5fd9b87408 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -24,9 +24,6 @@ #include "RenderableEntityItem.h" #include -#if 0 -#include -#endif namespace render { namespace entities { class ZoneEntityRenderer : public TypedEntityRenderer { @@ -40,7 +37,6 @@ protected: virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override; virtual ItemKey getKey() override; virtual void doRender(RenderArgs* args) override; - virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; @@ -76,14 +72,7 @@ private: glm::vec3 _lastPosition; glm::vec3 _lastDimensions; glm::quat _lastRotation; - - // FIXME compount shapes are currently broken - // FIXME draw zone boundaries are currently broken (also broken in master) -#if 0 - ModelPointer _model; - bool _lastModelActive { false }; - QString _lastShapeURL; -#endif + bool _lastVisible; LightStagePointer _stage; const graphics::LightPointer _sunLight { std::make_shared() }; @@ -137,25 +126,4 @@ private: } } // namespace -#if 0 - -class NetworkGeometry; -class KeyLightPayload; - -class RenderableZoneEntityItemMeta; - -class RenderableZoneEntityItem : public ZoneEntityItem, public RenderableEntityInterface { -public: - virtual bool contains(const glm::vec3& point) const override; - virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; -private: - virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true) override { EntityItem::locationChanged(tellPhysics, tellChildren); notifyBoundChanged(); } - virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } - void notifyBoundChanged(); - void notifyChangedRenderItem(); - void sceneUpdateRenderItemFromEntity(render::Transaction& transaction); -}; -#endif - #endif // hifi_RenderableZoneEntityItem_h diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 0e9672a7f2..8403d87868 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -43,7 +43,7 @@ const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" }; void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& output) { // Filter out the sorted list of zones - const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION.c_str()); + const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION); // just setup the current zone env task.addJob("SetupZones", zoneItems); diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 4f6fe3e36a..ff848aa464 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -277,6 +277,9 @@ void Scene::processTransactionFrame(const Transaction& transaction) { // removes removeItems(transaction._removedItems); + // handle selections + resetSelections(transaction._resetSelections); + // add transitions transitionItems(transaction._addedTransitions); reApplyTransitions(transaction._reAppliedTransitions); @@ -287,13 +290,6 @@ void Scene::processTransactionFrame(const Transaction& transaction) { _numAllocatedItems.exchange(maxID); } - if (transaction.touchTransactions()) { - std::unique_lock lock(_selectionsMutex); - - // resets and potential NEW items - resetSelections(transaction._resetSelections); - } - resetHighlights(transaction._highlightResets); removeHighlights(transaction._highlightRemoves); queryHighlights(transaction._highlightQueries); @@ -581,35 +577,34 @@ void Scene::resetItemTransition(ItemID itemId) { } } -// This function is thread safe Selection Scene::getSelection(const Selection::Name& name) const { std::unique_lock lock(_selectionsMutex); auto found = _selections.find(name); if (found == _selections.end()) { return Selection(); } else { - return (*found).second; + return found->second; } } -// This function is thread safe bool Scene::isSelectionEmpty(const Selection::Name& name) const { std::unique_lock lock(_selectionsMutex); auto found = _selections.find(name); if (found == _selections.end()) { return true; } else { - return (*found).second.isEmpty(); + return found->second.isEmpty(); } } void Scene::resetSelections(const Transaction::SelectionResets& transactions) { + std::unique_lock lock(_selectionsMutex); for (auto selection : transactions) { auto found = _selections.find(selection.getName()); if (found == _selections.end()) { - _selections.insert(SelectionMap::value_type(selection.getName(), selection)); + _selections[selection.getName()] = selection; } else { - (*found).second = selection; + found->second = selection; } } } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 08fbf33bcd..5f4a209b66 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -78,9 +78,6 @@ public: void merge(Transaction&& transaction); void clear(); - // Checkers if there is work to do when processing the transaction - bool touchTransactions() const { return !_resetSelections.empty(); } - protected: using Reset = std::tuple; diff --git a/libraries/render/src/render/Selection.h b/libraries/render/src/render/Selection.h index 2c7ce710cd..05b2395b42 100644 --- a/libraries/render/src/render/Selection.h +++ b/libraries/render/src/render/Selection.h @@ -45,9 +45,8 @@ namespace render { Name _name; ItemIDs _items; }; - using Selections = std::vector; - using SelectionMap = std::map; + using SelectionMap = std::unordered_map; } From 04198c6bfcb429373d2ae07e115872ca7c63f403 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 8 May 2019 11:36:44 -0700 Subject: [PATCH 42/83] use weak ptrs, add comment about SelectSortItems performance --- .../entities-renderer/src/EntityTreeRenderer.cpp | 12 +++++++----- libraries/entities-renderer/src/EntityTreeRenderer.h | 4 ++-- libraries/render-utils/src/ZoneRenderer.cpp | 2 ++ libraries/render/src/render/Scene.cpp | 5 ++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 61aa3b5d2d..e8ce812285 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1191,7 +1191,8 @@ bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& auto it = c.begin(); while (it != c.end()) { - if (!(it->zone->isLocalEntity() || (it->zone->isAvatarEntity() && it->zone->getOwningAvatarID() == sessionUUID))) { + auto zone = it->zone.lock(); + if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) { zonesChanged = true; it = c.erase(it); } else { @@ -1208,8 +1209,9 @@ bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& std::pair EntityTreeRenderer::LayeredZones::getZoneInteractionProperties() const { auto it = c.cbegin(); while (it != c.cend()) { - if (it->zone && it->zone->isDomainEntity()) { - return { it->zone->getFlyingAllowed(), it->zone->getGhostingAllowed() }; + auto zone = it->zone.lock(); + if (zone && zone->isDomainEntity()) { + return { zone->getFlyingAllowed(), zone->getGhostingAllowed() }; } it++; } @@ -1219,7 +1221,7 @@ std::pair EntityTreeRenderer::LayeredZones::getZoneInteractionProper void EntityTreeRenderer::LayeredZones::remove(const std::shared_ptr& zone) { auto it = c.begin(); while (it != c.end()) { - if (it->zone == zone) { + if (it->zone.lock() == zone) { break; } it++; @@ -1262,7 +1264,7 @@ bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const { void EntityTreeRenderer::LayeredZones::appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const { auto it = c.cbegin(); while (it != c.cend()) { - if (it->zone) { + if (it->zone.lock()) { auto id = entityTreeRenderer->renderableIdForEntityId(it->id); if (id != render::Item::INVALID_ITEM_ID) { list.push_back(id); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index f7f5ae9f22..f4284078a3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -213,11 +213,11 @@ private: LayeredZone(std::shared_ptr zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {} bool operator>(const LayeredZone& r) const { return volume > r.volume; } - bool operator==(const LayeredZone& r) const { return zone.get() == r.zone.get(); } + bool operator==(const LayeredZone& r) const { return zone.lock() == r.zone.lock(); } bool operator!=(const LayeredZone& r) const { return !(*this == r); } bool operator>=(const LayeredZone& r) const { return (*this > r) || (*this == r); } - std::shared_ptr zone; + std::weak_ptr zone; QUuid id; float volume; }; diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 8403d87868..e1606ba9f8 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -43,6 +43,8 @@ const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" }; void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& output) { // Filter out the sorted list of zones + // FIXME: the zones in the selection are already sorted, but we're doing another sort here to pick the selected items + // out of `input`, which means we're also looping over the inItems an extra time. const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION); // just setup the current zone env diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index ff848aa464..3fd52d5fa2 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -277,9 +277,6 @@ void Scene::processTransactionFrame(const Transaction& transaction) { // removes removeItems(transaction._removedItems); - // handle selections - resetSelections(transaction._resetSelections); - // add transitions transitionItems(transaction._addedTransitions); reApplyTransitions(transaction._reAppliedTransitions); @@ -290,6 +287,8 @@ void Scene::processTransactionFrame(const Transaction& transaction) { _numAllocatedItems.exchange(maxID); } + resetSelections(transaction._resetSelections); + resetHighlights(transaction._highlightResets); removeHighlights(transaction._highlightRemoves); queryHighlights(transaction._highlightQueries); From c3730e41f7b17e6c6c247ffa2d91911b66d49ec2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 9 May 2019 08:33:37 +1200 Subject: [PATCH 43/83] Typos --- interface/src/raypick/ParabolaPick.h | 2 +- interface/src/raypick/PickScriptingInterface.cpp | 12 ++++++------ interface/src/raypick/PickScriptingInterface.h | 4 ++-- interface/src/raypick/RayPickScriptingInterface.h | 2 +- libraries/shared/src/PickFilter.h | 2 +- libraries/shared/src/RegisteredMetaTypes.h | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/raypick/ParabolaPick.h b/interface/src/raypick/ParabolaPick.h index 78c35e13b4..1175b2b246 100644 --- a/interface/src/raypick/ParabolaPick.h +++ b/interface/src/raypick/ParabolaPick.h @@ -50,7 +50,7 @@ public: * @property {boolean} intersects - true if there's a valid intersection, false if there isn't. * @property {Uuid} objectID - The ID of the intersected object. null for HUD or invalid intersections. * @property {number} distance - The distance from the parabola origin to the intersection point in a straight line. - * @property {number} parabolicDistance - The distance from the parabola origin to the intersection point along the arc of + * @property {number} parabolicDistance - The distance from the parabola origin to the intersection point along the arc of * the parabola. * @property {Vec3} intersection - The intersection point in world coordinates. * @property {Vec3} surfaceNormal - The surface normal at the intersected point. All NaNs if type == diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 7e87ac22a7..dff68f3ded 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -64,12 +64,12 @@ PickFilter getPickFilter(unsigned int filter) { * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. - * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. * A value of 0 means no joint.
* Used only if parentID is specified. * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to - * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not * parented to anything.
* Used only if parentID is not specified. * @property {Vec3} [position=Vec3.ZERO] - The offset of the ray origin from its parent if parented, otherwise the ray origin @@ -198,12 +198,12 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties * values (e.g., Picks.PICK_DOMAIN_ENTTITIES) combined with | (bitwise OR) operators. * @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. - * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. * A value of 0 means no joint.
* Used only if parentID is specified. * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to - * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not * parented to anything. * Used only if parentID is not specified. * @property {Vec3} [position=Vec3.ZERO] - The offset of the parabola origin from its parent if parented, otherwise the @@ -307,12 +307,12 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti *

Note: Collision picks do not intersect the HUD.

* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of 0.0 * means no maximum. - * @property {Uuid} [parentID] - The ID of the parent: either an avatar, an entity, or another pick. + * @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint. * A value of 0 means no joint.
* Used only if parentID is specified. * @property {string} [joint] - "Mouse" parents the pick to the mouse; "Avatar" parents the pick to - * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise the pick is "static", not + * the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not * parented to anything.
* Used only if parentID is not specified. * @property {boolean} [scaleWithParent=true] - true to scale the pick's dimensions and threshold according to the diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index b85249b599..1cbdaa92f7 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -109,7 +109,7 @@ public: * Creates a new pick. Different {@link PickType}s use different properties, and within one PickType the properties you * choose can lead to a wide range of behaviors. For example, with PickType.Ray, the properties could * configure a mouse ray pick, an avatar head ray pick, or a joint ray pick. - *

Warning: Picks created with this method currently always intersect at least visible and collidable + *

Warning: Picks created using this method currently always intersect at least visible and collidable * things but this may not always be the case.

* @function Picks.createPick * @param {PickType} type - The type of picking to use. @@ -121,7 +121,7 @@ public: Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); /**jsdoc - * Enables a pick. enabled picks update their pick results. + * Enables a pick. Enabled picks update their pick results. * @function Picks.enablePick * @param {number} id - The ID of the pick. */ diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index a38e953824..32a2ec4a5d 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -67,7 +67,7 @@ public: /**jsdoc * Creates a new ray pick. - *

Warning: Picks created with this method currently always intersect at least visible and collidable + *

Warning: Picks created using this method currently always intersect at least visible and collidable * things but this may not always be the case.

* @function RayPick.createRayPick * @param {Picks.RayPickProperties} properties - Properties of the pick. diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 8c1c4f56f6..9b7f763e6a 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -19,7 +19,7 @@ public: * individual flag values.

*
Report when the rendering of avatars changes.
* - * + * * * * diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ef2d775eec..25a1792fe2 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -296,7 +296,7 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); * @typedef {object} StylusTip * @property {number} side - The hand that the stylus is attached to: 0 for left hand, 1 for the * right hand, -1 for invalid. - * @property {Vec3} tipOffset - The position of the stylus tip relative to the body of the stylus. + * @property {Vec3} tipOffset - The position of the stylus tip relative to the body of the stylus. * @property {Vec3} position - The position of the stylus tip. * @property {Quat} orientation - The orientation of the stylus. * @property {Vec3} velocity - The velocity of the stylus tip. From 78403e443609612b440a2c141d7fa34492b02d1c Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 8 May 2019 13:33:45 -0700 Subject: [PATCH 44/83] Add gpu_name, gpu_driver & gpu_memory to crash annotations This may help us identify the hardware causing early gl initialization errors. --- interface/src/Application.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b13fd3dda9..a0a660b21d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1069,6 +1069,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } } + { + // identify gpu as early as possible to help identify OpenGL initialization errors. + auto gpuIdent = GPUIdent::getInstance(); + setCrashAnnotation("gpu_name", gpuIdent->getName().toStdString()); + setCrashAnnotation("gpu_driver", gpuIdent->getDriver().toStdString()); + setCrashAnnotation("gpu_memory", std::to_string(gpuIdent->getMemory())); + } + // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); From f60dbda25e5e0293918b54f44f7965cbdbe02d6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 8 May 2019 14:48:49 -0700 Subject: [PATCH 45/83] clear all HARD flags when reinserting object in PhysicsEngine --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 08ea98eef1..daa2b5d954 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -430,7 +430,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio } if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) { transaction.objectsToReinsert.push_back(object); - handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); + handledFlags |= HARD_DIRTY_PHYSICS_FLAGS; } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } From 0ddfd6fe2b516f89ac0f0bc266c9a01f49eb0988 Mon Sep 17 00:00:00 2001 From: Matt Hardcastle Date: Wed, 8 May 2019 10:08:02 -0700 Subject: [PATCH 46/83] Add FLock to lock files another way The build system uses a class called Singleton to handle file locking. This class has a few potential issues: 1) An instance of Single could delete a file owned by another instance 2) Calling `print` in `__enter__` doesn't write to STDOUT 3) If the lock file already exists but the process that "owned" it died without deleting it every process will be locked This change is an attempt to resolve these issues and provide some lock debugging code. The code is structured to default to using the current Singleton class unless the USE_FLOCK_CLS environmental variable is set. In the event the environment variable is set the Singleton class is replaced with FLock. The FLock class is designed to work while running on a machine that's also using the real Singleton class. It does this at the expense of not solving issue number #1. If FLock is proven successful and completely deployed to the cluster it should be made the default and the code that deletes the lock file in `__exit__` should be removed. --- hifi_singleton.py | 118 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/hifi_singleton.py b/hifi_singleton.py index 692948c80b..70871a9f60 100644 --- a/hifi_singleton.py +++ b/hifi_singleton.py @@ -1,3 +1,5 @@ +import json +import logging import os import platform import time @@ -7,6 +9,15 @@ try: except ImportError: fcntl = None +try: + import msvcrt +except ImportError: + msvcrt = None + + +logger = logging.getLogger(__name__) + + # Used to ensure only one instance of the script runs at a time class Singleton: def __init__(self, path): @@ -33,7 +44,10 @@ class Singleton: else: self.fh.close() self.fh = None - print("Couldn't aquire lock, retrying in 10 seconds") + # print is horked here so write directly to stdout. + with open(1, mode="w", closefd=False) as _stdout: + _stdout.write("Couldn't aquire lock, retrying in 10 seconds\n") + _stdout.flush() time.sleep(10) return self @@ -43,4 +57,104 @@ class Singleton: else: fcntl.lockf(self.fh, fcntl.LOCK_UN) self.fh.close() - os.unlink(self.path) \ No newline at end of file + os.unlink(self.path) + + +class FLock: + """ + File locking context manager + + >> with FLock("/tmp/foo.lock"): + >> do_something_that_must_be_synced() + + The lock file must stick around forever. The author is not aware of a no cross platform way to clean it up w/o introducting race conditions. + """ + def __init__(self, path): + self.fh = os.open(path, os.O_CREAT | os.O_RDWR) + self.path = path + + def _lock_posix(self): + try: + fcntl.lockf(self.fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + # Windows sleeps for 10 seconds before giving up on a lock. + # Lets mimic that behavior. + time.sleep(10) + return False + else: + return True + + def _lock_windows(self): + try: + msvcrt.locking(self.fh, msvcrt.LK_LOCK, 1) + except OSError: + return False + else: + return True + + if fcntl is not None: + _lock = _lock_posix + elif msvcrt is not None: + _lock = _lock_windows + else: + raise RuntimeError("No locking library found") + + def read_stats(self): + data = {} + with open(self.fh, mode="r", closefd=False) as stats_file: + stats_file.seek(0) + try: + data = json.loads(stats_file.read()) + except json.decoder.JSONDecodeError: + logger.warning("couldn't decode json in lock file") + except PermissionError: + # Can't read a locked file on Windows :( + pass + + lock_age = time.time() - os.fstat(self.fh).st_mtime + if lock_age > 0: + data["Age"] = "%0.2f" % lock_age + + with open(1, mode="w", closefd=False) as _stdout: + _stdout.write("Lock stats:\n") + for key, value in sorted(data.items()): + _stdout.write("* %s: %s\n" % (key, value)) + _stdout.flush() + + def write_stats(self): + stats = { + "Owner PID": os.getpid(), + } + flock_env_vars = os.getenv("FLOCK_ENV_VARS") + if flock_env_vars: + for env_var_name in flock_env_vars.split(":"): + stats[env_var_name] = os.getenv(env_var_name) + + with open(self.fh, mode="w", closefd=False) as stats_file: + stats_file.truncate() + return stats_file.write(json.dumps(stats, indent=2)) + + def __enter__(self): + while not self._lock(): + try: + self.read_stats() + except (IOError, ValueError) as exc: + logger.exception("couldn't read stats") + time.sleep(3.33) # don't hammer the file + + self.write_stats() + + return self + + def __exit__(self, type, value, traceback): + os.close(self.fh) + # WARNING: `os.close` gives up the lock on `fh` then we attempt the `os.unlink`. On posix platforms this can lead to us deleting a lock file that another process owns. This step is required to maintain compatablity with Singleton. When and if FLock is completely rolled out to the build fleet this unlink should be removed. + try: + os.unlink(self.path) + except (FileNotFoundError, PermissionError): + logger.exception("couldn't unlink lock file") + + +if os.getenv("USE_FLOCK_CLS") is not None: + logger.warning("Using FLock locker") + Singleton = FLock From b3d0705e39db65c42dd5afbf215c4666fa09561b Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 8 May 2019 18:38:00 -0700 Subject: [PATCH 47/83] Extend ICE logging + make it more searchable --- assignment-client/src/AssignmentClient.cpp | 2 +- domain-server/src/DomainGatekeeper.cpp | 19 +++---- domain-server/src/DomainServer.cpp | 45 ++++++++-------- domain-server/src/DomainServer.h | 1 + interface/src/Application.cpp | 6 +-- libraries/networking/src/AddressManager.cpp | 2 +- libraries/networking/src/DomainHandler.cpp | 52 ++++++++++++------- libraries/networking/src/DomainHandler.h | 6 +-- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/NetworkLogging.cpp | 1 + libraries/networking/src/NetworkLogging.h | 1 + libraries/networking/src/NodeList.cpp | 30 +++++------ libraries/networking/src/NodeList.h | 4 +- .../networking/src/ThreadedAssignment.cpp | 2 +- tools/ac-client/src/ACClientApp.cpp | 2 +- tools/atp-client/src/ATPClientApp.cpp | 2 +- 16 files changed, 95 insertions(+), 82 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index c1943de2cc..1ef375b562 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -307,7 +307,7 @@ void AssignmentClient::assignmentCompleted() { // reset our NodeList by switching back to unassigned and clearing the list nodeList->setOwnerType(NodeType::Unassigned); - nodeList->reset(); + nodeList->reset("Assignment completed"); nodeList->resetNodeInterestSet(); _isAssigned = false; diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 8c7beaa614..e53196c67f 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -811,26 +811,23 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointergetMessage()); - NetworkPeer* receivedPeer = new NetworkPeer; + auto receivedPeer = SharedNetworkPeer::create(); iceResponseStream >> *receivedPeer; if (!_icePeers.contains(receivedPeer->getUUID())) { - qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer; - SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer); - _icePeers[receivedPeer->getUUID()] = newPeer; + qCDebug(domain_server_ice) << "New peer requesting ICE connection being added to hash -" << *receivedPeer; + _icePeers[receivedPeer->getUUID()] = receivedPeer; // make sure we know when we should ping this peer - connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout); + connect(receivedPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout); // immediately ping the new peer, and start a timer to continue pinging it until we connect to it - newPeer->startPingTimer(); + receivedPeer->startPingTimer(); - qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" - << newPeer->getUUID(); + qCDebug(domain_server_ice) << "Sending ping packets to establish connectivity with ICE peer with ID" + << receivedPeer->getUUID(); - pingPunchForConnectingPeer(newPeer); - } else { - delete receivedPeer; + pingPunchForConnectingPeer(receivedPeer); } } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f73c59dc32..bef6723912 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -58,6 +58,7 @@ #include Q_LOGGING_CATEGORY(domain_server, "hifi.domain_server") +Q_LOGGING_CATEGORY(domain_server_ice, "hifi.domain_server.ice") const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token"; const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace"; @@ -374,7 +375,7 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) { } if (_iceServerAddr.isEmpty()) { - qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString; + qCWarning(domain_server_ice) << "Could not parse an IP address and port combination from" << hostnamePortString; ::exit(0); } } @@ -1570,12 +1571,8 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate"; callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate"; - static bool printedIceServerMessage = false; - if (!printedIceServerMessage) { - printedIceServerMessage = true; - qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" - << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); - } + qCDebug(domain_server_ice) << "Updating ice-server address in High Fidelity Metaverse API to" + << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address"; @@ -1589,11 +1586,11 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply) { _sendICEServerAddressToMetaverseAPIInProgress = false; if (_sendICEServerAddressToMetaverseAPIRedo) { - qDebug() << "ice-server address updated with metaverse, but has since changed. redoing update..."; + qCDebug(domain_server_ice) << "ice-server address updated with metaverse, but has since changed. redoing update..."; _sendICEServerAddressToMetaverseAPIRedo = false; sendICEServerAddressToMetaverseAPI(); } else { - qDebug() << "ice-server address updated with metaverse."; + qCDebug(domain_server_ice) << "ice-server address updated with metaverse."; } } @@ -1606,9 +1603,9 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestRepl } else { const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000; - qWarning() << "Failed to update ice-server address with High Fidelity Metaverse - error was" + qCWarning(domain_server_ice) << "Failed to update ice-server address with High Fidelity Metaverse - error was" << requestReply->errorString(); - qWarning() << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds"; + qCWarning(domain_server_ice) << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds"; QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI())); } @@ -1621,13 +1618,13 @@ void DomainServer::sendHeartbeatToIceServer() { auto limitedNodeList = DependencyManager::get(); if (!accountManager->getAccountInfo().hasPrivateKey()) { - qWarning() << "Cannot send an ice-server heartbeat without a private key for signature."; - qWarning() << "Waiting for keypair generation to complete before sending ICE heartbeat."; + qCWarning(domain_server_ice) << "Cannot send an ice-server heartbeat without a private key for signature."; + qCWarning(domain_server_ice) << "Waiting for keypair generation to complete before sending ICE heartbeat."; if (!limitedNodeList->getSessionUUID().isNull()) { accountManager->generateNewDomainKeypair(limitedNodeList->getSessionUUID()); } else { - qWarning() << "Attempting to send ICE server heartbeat with no domain ID. This is not supported"; + qCWarning(domain_server_ice) << "Attempting to send ICE server heartbeat with no domain ID. This is not supported"; } return; @@ -1639,8 +1636,8 @@ void DomainServer::sendHeartbeatToIceServer() { ++_noReplyICEHeartbeats; if (_noReplyICEHeartbeats > FAILOVER_NO_REPLY_ICE_HEARTBEATS) { - qWarning() << "There have been" << _noReplyICEHeartbeats - 1 << "heartbeats sent with no reply from the ice-server"; - qWarning() << "Clearing the current ice-server socket and selecting a new candidate ice-server"; + qCWarning(domain_server_ice) << "There have been" << _noReplyICEHeartbeats - 1 << "heartbeats sent with no reply from the ice-server"; + qCWarning(domain_server_ice) << "Clearing the current ice-server socket and selecting a new candidate ice-server"; // add the current address to our list of failed addresses _failedIceServerAddresses << _iceServerSocket.getAddress(); @@ -1713,8 +1710,8 @@ void DomainServer::sendHeartbeatToIceServer() { limitedNodeList->sendUnreliablePacket(*_iceServerHeartbeatPacket, _iceServerSocket); } else { - qDebug() << "Not sending ice-server heartbeat since there is no selected ice-server."; - qDebug() << "Waiting for" << _iceServerAddr << "host lookup response"; + qCDebug(domain_server_ice) << "Not sending ice-server heartbeat since there is no selected ice-server."; + qCDebug(domain_server_ice) << "Waiting for" << _iceServerAddr << "host lookup response"; } } @@ -3294,7 +3291,7 @@ void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN) { - qDebug() << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server" + qCDebug(domain_server_ice) << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server" << "- re-generating keypair now"; // we've hit our threshold of heartbeat denials, trigger a keypair re-generation @@ -3316,7 +3313,7 @@ void DomainServer::processICEServerHeartbeatACK(QSharedPointer if (!_connectedToICEServer) { _connectedToICEServer = true; sendICEServerAddressToMetaverseAPI(); - qInfo() << "Connected to ice-server at" << _iceServerSocket; + qCInfo(domain_server_ice) << "Connected to ice-server at" << _iceServerSocket; } } @@ -3347,7 +3344,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) { } if (hostInfo.error() != QHostInfo::NoError || sanitizedAddresses.empty()) { - qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString(); + qCWarning(domain_server_ice) << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString(); // if we don't have an ICE server to use yet, trigger a retry if (_iceServerSocket.isNull()) { @@ -3362,7 +3359,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) { _iceServerAddresses = sanitizedAddresses; if (countBefore == 0) { - qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr; + qCInfo(domain_server_ice) << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr; } if (_iceServerSocket.isNull()) { @@ -3396,7 +3393,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) { // we ended up with an empty list since everything we've tried has failed // so clear the set of failed addresses and start going through them again - qWarning() << "All current ice-server addresses have failed - re-attempting all current addresses for" + qCWarning(domain_server_ice) << "All current ice-server addresses have failed - re-attempting all current addresses for" << _iceServerAddr; _failedIceServerAddresses.clear(); @@ -3416,7 +3413,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) { } _iceServerSocket = HifiSockAddr { candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT }; - qInfo() << "Set candidate ice-server socket to" << _iceServerSocket; + qCInfo(domain_server_ice) << "Set candidate ice-server socket to" << _iceServerSocket; // clear our number of hearbeat denials, this should be re-set on ice-server change _numHeartbeatDenials = 0; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index f0c20241a2..8276566233 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -40,6 +40,7 @@ #include Q_DECLARE_LOGGING_CATEGORY(domain_server) +Q_DECLARE_LOGGING_CATEGORY(domain_server_ice) typedef QSharedPointer SharedAssignmentPointer; typedef QMultiHash TransactionHash; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d84c3e8b85..baa0e2037f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2688,7 +2688,7 @@ void Application::cleanupBeforeQuit() { auto nodeList = DependencyManager::get(); // send the domain a disconnect packet, force stoppage of domain-server check-ins - nodeList->getDomainHandler().disconnect(); + nodeList->getDomainHandler().disconnect("Quitting"); nodeList->setIsShuttingDown(true); // tell the packet receiver we're shutting down, so it can drop packets @@ -5537,7 +5537,7 @@ void Application::pauseUntilLoginDetermined() { cameraModeChanged(); // disconnect domain handler. - nodeList->getDomainHandler().disconnect(); + nodeList->getDomainHandler().disconnect("Pause until login determined"); // From now on, it's permissible to call resumeAfterLoginDialogActionTaken() _resumeAfterLoginDialogActionTaken_SafeToRun = true; @@ -5916,7 +5916,7 @@ void Application::reloadResourceCaches() { DependencyManager::get()->refreshAll(); DependencyManager::get()->refreshAll(); - DependencyManager::get()->reset(); // Force redownload of .fst models + DependencyManager::get()->reset("Reloading resources"); // Force redownload of .fst models DependencyManager::get()->reloadAllScripts(); getOffscreenUI()->clearCache(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 517daf8ce5..7b21cb3460 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -482,7 +482,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); - qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID + qCDebug(networking_ice) << "Possible domain change required to connect to domain with ID" << domainID << "via ice-server at" << iceServerAddress; emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 2513510b05..7793963959 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -64,7 +64,7 @@ DomainHandler::DomainHandler(QObject* parent) : connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); } -void DomainHandler::disconnect() { +void DomainHandler::disconnect(QString reason) { // if we're currently connected to a domain, send a disconnect packet on our way out if (_isConnected) { sendDisconnectPacket(); @@ -81,6 +81,8 @@ void DomainHandler::disconnect() { _sockAddr.clear(); } + qCDebug(networking_ice) << "Disconnecting from domain server."; + qCDebug(networking_ice) << "REASON:" << reason; setIsConnected(false); } @@ -100,9 +102,9 @@ void DomainHandler::clearSettings() { _settingsObject = QJsonObject(); } -void DomainHandler::softReset() { +void DomainHandler::softReset(QString reason) { qCDebug(networking) << "Resetting current domain connection information."; - disconnect(); + disconnect(reason); clearSettings(); @@ -118,10 +120,10 @@ void DomainHandler::softReset() { } } -void DomainHandler::hardReset() { +void DomainHandler::hardReset(QString reason) { emit resetting(); - softReset(); + softReset(reason); _isInErrorState = false; emit redirectErrorStateChanged(_isInErrorState); @@ -166,7 +168,7 @@ void DomainHandler::setErrorDomainURL(const QUrl& url) { void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) { if (_sockAddr != sockAddr) { // we should reset on a sockAddr change - hardReset(); + hardReset("Changing domain sockAddr"); // change the sockAddr _sockAddr = sockAddr; } @@ -209,7 +211,7 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { // if it's in the error state, reset and try again. if ((_domainURL != domainURL || _sockAddr.getPort() != domainPort) || _isInErrorState) { // re-set the domain info so that auth information is reloaded - hardReset(); + hardReset("Changing domain URL"); QString previousHost = _domainURL.host(); _domainURL = domainURL; @@ -242,10 +244,24 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) { + auto newIceServer = _iceServerSockAddr.getAddress().toString() != iceServerHostname; + auto newDomainID = id != _pendingDomainID; + // if it's in the error state, reset and try again. - if ((_iceServerSockAddr.getAddress().toString() != iceServerHostname || id != _pendingDomainID) || _isInErrorState) { + if (newIceServer || newDomainID || _isInErrorState) { + QString reason; + if (newIceServer) { + reason += "New ICE server;"; + } + if (newDomainID) { + reason += "New domain ID;"; + } + if (_isInErrorState) { + reason += "Domain in error state;"; + } + // re-set the domain info to connect to new domain - hardReset(); + hardReset(reason); // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); @@ -268,7 +284,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, completedIceServerHostnameLookup(); } - qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname; + qCDebug(networking_ice) << "ICE required to connect to domain via ice server at" << iceServerHostname; } } @@ -322,7 +338,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { } void DomainHandler::completedIceServerHostnameLookup() { - qCDebug(networking) << "ICE server socket is at" << _iceServerSockAddr; + qCDebug(networking_ice) << "ICE server socket is at" << _iceServerSockAddr; DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerSocket); @@ -409,7 +425,7 @@ void DomainHandler::processSettingsPacketList(QSharedPointer pa void DomainHandler::processICEPingReplyPacket(QSharedPointer message) { const HifiSockAddr& senderSockAddr = message->getSenderSockAddr(); - qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr; + qCDebug(networking_ice) << "Received reply from domain-server on" << senderSockAddr; if (getIP().isNull()) { // we're hearing back from this domain-server, no need to refresh API information @@ -417,13 +433,13 @@ void DomainHandler::processICEPingReplyPacket(QSharedPointer me // for now we're unsafely assuming this came back from the domain if (senderSockAddr == _icePeer.getLocalSocket()) { - qCDebug(networking) << "Connecting to domain using local socket"; + qCDebug(networking_ice) << "Connecting to domain using local socket"; activateICELocalSocket(); } else if (senderSockAddr == _icePeer.getPublicSocket()) { - qCDebug(networking) << "Conecting to domain using public socket"; + qCDebug(networking_ice) << "Conecting to domain using public socket"; activateICEPublicSocket(); } else { - qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect."; + qCDebug(networking_ice) << "Reply does not match either local or public socket for domain. Will not connect."; } } } @@ -442,7 +458,7 @@ void DomainHandler::processDTLSRequirementPacket(QSharedPointer void DomainHandler::processICEResponsePacket(QSharedPointer message) { if (_icePeer.hasSockets()) { - qCDebug(networking) << "Received an ICE peer packet for domain-server but we already have sockets. Not processing."; + qCDebug(networking_ice) << "Received an ICE peer packet for domain-server but we already have sockets. Not processing."; // bail on processing this packet if our ice peer already has sockets return; } @@ -457,10 +473,10 @@ void DomainHandler::processICEResponsePacket(QSharedPointer mes DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation); if (_icePeer.getUUID() != _pendingDomainID) { - qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; + qCDebug(networking_ice) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; _icePeer.reset(); } else { - qCDebug(networking) << "Received network peer object for domain -" << _icePeer; + qCDebug(networking_ice) << "Received network peer object for domain -" << _icePeer; // ask the peer object to start its ping timer _icePeer.startPingTimer(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 620ffb9641..fb18866001 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -42,7 +42,7 @@ class DomainHandler : public QObject { public: DomainHandler(QObject* parent = 0); - void disconnect(); + void disconnect(QString reason); void clearSettings(); const QUuid& getUUID() const { return _uuid; } @@ -105,7 +105,7 @@ public: bool isSocketKnown() const { return !_sockAddr.getAddress().isNull(); } - void softReset(); + void softReset(QString reason); int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; } bool checkInPacketTimeout(); @@ -210,7 +210,7 @@ signals: private: bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode); void sendDisconnectPacket(); - void hardReset(); + void hardReset(QString reason); bool isHardRefusal(int reasonCode); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 82f3459c15..cc0ef8ae17 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -1265,7 +1265,7 @@ void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSoc iceDataStream << peerID; - qCDebug(networking) << "Sending packet to ICE server to request connection info for peer with ID" + qCDebug(networking_ice) << "Sending packet to ICE server to request connection info for peer with ID" << uuidStringWithoutCurlyBraces(peerID); } diff --git a/libraries/networking/src/NetworkLogging.cpp b/libraries/networking/src/NetworkLogging.cpp index 834398694d..3d7c2fc5d5 100644 --- a/libraries/networking/src/NetworkLogging.cpp +++ b/libraries/networking/src/NetworkLogging.cpp @@ -12,6 +12,7 @@ #include "NetworkLogging.h" Q_LOGGING_CATEGORY(networking, "hifi.networking") +Q_LOGGING_CATEGORY(networking_ice, "hifi.networking.ice") Q_LOGGING_CATEGORY(resourceLog, "hifi.networking.resource") Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client") Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client") diff --git a/libraries/networking/src/NetworkLogging.h b/libraries/networking/src/NetworkLogging.h index 30116ff405..8247c60096 100644 --- a/libraries/networking/src/NetworkLogging.h +++ b/libraries/networking/src/NetworkLogging.h @@ -16,6 +16,7 @@ Q_DECLARE_LOGGING_CATEGORY(resourceLog) Q_DECLARE_LOGGING_CATEGORY(networking) +Q_DECLARE_LOGGING_CATEGORY(networking_ice) Q_DECLARE_LOGGING_CATEGORY(asset_client) Q_DECLARE_LOGGING_CATEGORY(messages_client) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0a4c63d712..5093cc8f62 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -243,9 +243,11 @@ void NodeList::processICEPingPacket(QSharedPointer message) { sendPacket(std::move(replyPacket), message->getSenderSockAddr()); } -void NodeList::reset(bool skipDomainHandlerReset) { +void NodeList::reset(QString reason, bool skipDomainHandlerReset) { if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, skipDomainHandlerReset)); + QMetaObject::invokeMethod(this, "reset", + Q_ARG(QString, reason), + Q_ARG(bool, skipDomainHandlerReset)); return; } @@ -267,7 +269,7 @@ void NodeList::reset(bool skipDomainHandlerReset) { if (!skipDomainHandlerReset) { // clear the domain connection information, unless they're the ones that asked us to reset - _domainHandler.softReset(); + _domainHandler.softReset(reason); } // refresh the owner UUID to the NULL UUID @@ -297,12 +299,12 @@ void NodeList::sendDomainServerCheckIn() { // may be called by multiple threads. if (!_sendDomainServerCheckInEnabled) { - qCDebug(networking) << "Refusing to send a domain-server check in while it is disabled."; + qCDebug(networking_ice) << "Refusing to send a domain-server check in while it is disabled."; return; } if (_isShuttingDown) { - qCDebug(networking) << "Refusing to send a domain-server check in while shutting down."; + qCDebug(networking_ice) << "Refusing to send a domain-server check in while shutting down."; return; } @@ -311,9 +313,9 @@ void NodeList::sendDomainServerCheckIn() { if (publicSockAddr.isNull()) { // we don't know our public socket and we need to send it to the domain server - qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; + qCDebug(networking_ice) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; } else if (domainHandlerIp.isNull() && _domainHandler.requiresICE()) { - qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in."; + qCDebug(networking_ice) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in."; handleICEConnectionToDomainServer(); // let the domain handler know we are due to send a checkin packet } else if (!domainHandlerIp.isNull() && !_domainHandler.checkInPacketTimeout()) { @@ -324,7 +326,7 @@ void NodeList::sendDomainServerCheckIn() { if (!domainIsConnected) { auto hostname = _domainHandler.getHostname(); - qCDebug(networking) << "Sending connect request to domain-server at" << hostname; + qCDebug(networking_ice) << "Sending connect request to domain-server at" << hostname; // is this our localhost domain-server? // if so we need to make sure we have an up-to-date local port in case it restarted @@ -334,7 +336,7 @@ void NodeList::sendDomainServerCheckIn() { quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, domainPort); - qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort; + qCDebug(networking_ice) << "Local domain-server port read from shared memory (or default) is" << domainPort; _domainHandler.setPort(domainPort); } } @@ -346,7 +348,7 @@ void NodeList::sendDomainServerCheckIn() { bool requiresUsernameSignature = !domainIsConnected && !connectionToken.isNull(); if (requiresUsernameSignature && !accountManager->getAccountInfo().hasPrivateKey()) { - qWarning() << "A keypair is required to present a username signature to the domain-server" + qCWarning(networking_ice) << "A keypair is required to present a username signature to the domain-server" << "but no keypair is present. Waiting for keypair generation to complete."; accountManager->generateNewUserKeypair(); @@ -574,12 +576,12 @@ void NodeList::pingPunchForDomainServer() { const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS; if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) { - qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" + qCDebug(networking_ice) << "Sending ping packets to establish connectivity with domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getPendingDomainID()); } else { if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat - qCDebug(networking) << "No ping replies received from domain-server with ID" + qCDebug(networking_ice) << "No ping replies received from domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getICEClientID()) << "-" << "re-sending ICE query."; _domainHandler.getICEPeer().softReset(); @@ -657,10 +659,8 @@ void NodeList::processDomainServerList(QSharedPointer message) if (_domainHandler.isConnected() && ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || (!currentSessionID.isNull() && newUUID != currentSessionID))) { - qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; - // reset the nodelist, but don't do a domain handler reset since we're about to process a good domain list - reset(true); + reset("Local ID or Session ID changed while connected to domain - forcing NodeList reset", true); // tell the domain handler that we're no longer connected so that below // it can re-perform actions as if we just connected diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index f871560fba..c377ea89cb 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -103,8 +103,8 @@ public: virtual HifiSockAddr getDomainSockAddr() const override { return _domainHandler.getSockAddr(); } public slots: - void reset(bool skipDomainHandlerReset = false); - void resetFromDomainHandler() { reset(true); } + void reset(QString reason, bool skipDomainHandlerReset = false); + void resetFromDomainHandler() { reset("Reset from Domain Handler", true); } void sendDomainServerCheckIn(); void handleDSPathQuery(const QString& newPath); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 9b9a53b469..6a29613d25 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -58,7 +58,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { packetReceiver.setShouldDropPackets(true); // send a disconnect packet to the domain - nodeList->getDomainHandler().disconnect(); + nodeList->getDomainHandler().disconnect("Finished"); // stop our owned timers _domainServerTimer.stop(); diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index cfede87c53..6b781a5bea 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -263,7 +263,7 @@ void ACClientApp::finish(int exitCode) { auto nodeList = DependencyManager::get(); // send the domain a disconnect packet, force stoppage of domain-server check-ins - nodeList->getDomainHandler().disconnect(); + nodeList->getDomainHandler().disconnect("Finishing"); nodeList->setIsShuttingDown(true); // tell the packet receiver we're shutting down, so it can drop packets diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index c688ba9c82..8c2fd44adf 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -394,7 +394,7 @@ void ATPClientApp::finish(int exitCode) { auto nodeList = DependencyManager::get(); // send the domain a disconnect packet, force stoppage of domain-server check-ins - nodeList->getDomainHandler().disconnect(); + nodeList->getDomainHandler().disconnect("Finishing"); nodeList->setIsShuttingDown(true); // tell the packet receiver we're shutting down, so it can drop packets From 5c42838261149441e1db97a7215b8fa844d50f1e Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 8 May 2019 18:38:35 -0700 Subject: [PATCH 48/83] Change timeouts to try to prevent ICE disconnects --- domain-server/src/DomainServer.cpp | 2 +- libraries/networking/src/NetworkPeer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bef6723912..24e2a48c2d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1630,7 +1630,7 @@ void DomainServer::sendHeartbeatToIceServer() { return; } - const int FAILOVER_NO_REPLY_ICE_HEARTBEATS { 3 }; + const int FAILOVER_NO_REPLY_ICE_HEARTBEATS { 6 }; // increase the count of no reply ICE heartbeats and check the current value ++_noReplyICEHeartbeats; diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 43fbc753eb..e2149d64af 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -23,7 +23,7 @@ const QString ICE_SERVER_HOSTNAME = "localhost"; const quint16 ICE_SERVER_DEFAULT_PORT = 7337; -const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000; +const int ICE_HEARBEAT_INTERVAL_MSECS = 1 * 1000; const int MAX_ICE_CONNECTION_ATTEMPTS = 5; const int UDP_PUNCH_PING_INTERVAL_MS = 250; From 4a4a92c009662daced888d38a26cb141025a17d0 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 6 May 2019 15:46:06 -0700 Subject: [PATCH 49/83] BUGZ-85 - audio pipeline interpolation on ring buffer starve Kick the PLC so that it generates a more pleasing 'fade' frame when ring buffer starves. --- libraries/audio/src/AudioSRC.cpp | 4 ++-- libraries/audio/src/AudioSRC.h | 3 +++ libraries/audio/src/InboundAudioStream.cpp | 11 +++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp index d488eccb6a..1accc8565d 100644 --- a/libraries/audio/src/AudioSRC.cpp +++ b/libraries/audio/src/AudioSRC.cpp @@ -1491,7 +1491,7 @@ AudioSRC::~AudioSRC() { // int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { int outputFrames = 0; - + QMutexLocker lock(&_renderMutex); while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); @@ -1516,7 +1516,7 @@ int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { // int AudioSRC::render(const float* input, float* output, int inputFrames) { int outputFrames = 0; - + QMutexLocker lock(&_renderMutex); while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); diff --git a/libraries/audio/src/AudioSRC.h b/libraries/audio/src/AudioSRC.h index d5971e5838..5299d9da7f 100644 --- a/libraries/audio/src/AudioSRC.h +++ b/libraries/audio/src/AudioSRC.h @@ -13,6 +13,7 @@ #define hifi_AudioSRC_h #include +#include static const int SRC_MAX_CHANNELS = 4; @@ -55,6 +56,8 @@ public: int getMaxInput(int outputFrames); private: + QMutex _renderMutex; + float* _polyphaseFilter; int* _stepTable; diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 8c5388e222..9844159c1a 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -215,7 +215,6 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { if (framesAvailable > _desiredJitterBufferFrames + MAX_FRAMES_OVER_DESIRED) { int framesToDrop = framesAvailable - (_desiredJitterBufferFrames + DESIRED_JITTER_BUFFER_FRAMES_PADDING); _ringBuffer.shiftReadPosition(framesToDrop * _ringBuffer.getNumFrameSamples()); - _framesAvailableStat.reset(); _currentJitterBufferFrames = 0; @@ -250,7 +249,7 @@ int InboundAudioStream::lostAudioData(int numPackets) { if (_decoder) { _decoder->lostFrame(decodedBuffer); } else { - decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO); + decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * _numChannels); memset(decodedBuffer.data(), 0, decodedBuffer.size()); } _ringBuffer.writeData(decodedBuffer.data(), decodedBuffer.size()); @@ -338,10 +337,14 @@ int InboundAudioStream::popSamples(int maxSamples, bool allOrNothing) { popSamplesNoCheck(samplesAvailable); samplesPopped = samplesAvailable; } else { - // we can't pop any samples, set this stream to starved + // we can't pop any samples, set this stream to starved for jitter + // buffer calculations. setToStarved(); _consecutiveNotMixedCount++; - _lastPopSucceeded = false; + //Kick PLC to generate a filler frame, reducing 'click' + lostAudioData(allOrNothing ? (maxSamples - samplesAvailable) / _ringBuffer.getNumFrameSamples() : 1); + samplesPopped = _ringBuffer.samplesAvailable(); + popSamplesNoCheck(samplesPopped); } } return samplesPopped; From dc7ec355444cc78307661576a3d2d499de7f7ac2 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 8 May 2019 16:17:18 -0700 Subject: [PATCH 50/83] BUGZ-85 - handle thread safety issues with calling the decoder from the real-time thread. --- .../src/audio/AvatarAudioStream.cpp | 1 + libraries/audio/src/AudioSRC.cpp | 2 - libraries/audio/src/AudioSRC.h | 2 - libraries/audio/src/InboundAudioStream.cpp | 54 +++++++++++++++---- libraries/audio/src/InboundAudioStream.h | 1 + .../audio/src/MixedProcessedAudioStream.cpp | 15 +++++- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index 1b3ca9a8b1..24b14ac9e5 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -45,6 +45,7 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); // restart the codec if (_codec) { + QMutexLocker lock(&_decoderMutex); if (_decoder) { _codec->releaseDecoder(_decoder); } diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp index 1accc8565d..867416871e 100644 --- a/libraries/audio/src/AudioSRC.cpp +++ b/libraries/audio/src/AudioSRC.cpp @@ -1491,7 +1491,6 @@ AudioSRC::~AudioSRC() { // int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { int outputFrames = 0; - QMutexLocker lock(&_renderMutex); while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); @@ -1516,7 +1515,6 @@ int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { // int AudioSRC::render(const float* input, float* output, int inputFrames) { int outputFrames = 0; - QMutexLocker lock(&_renderMutex); while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); diff --git a/libraries/audio/src/AudioSRC.h b/libraries/audio/src/AudioSRC.h index 5299d9da7f..111c2105b7 100644 --- a/libraries/audio/src/AudioSRC.h +++ b/libraries/audio/src/AudioSRC.h @@ -13,7 +13,6 @@ #define hifi_AudioSRC_h #include -#include static const int SRC_MAX_CHANNELS = 4; @@ -56,7 +55,6 @@ public: int getMaxInput(int outputFrames); private: - QMutex _renderMutex; float* _polyphaseFilter; int* _stepTable; diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 9844159c1a..482991626a 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -246,12 +246,20 @@ int InboundAudioStream::lostAudioData(int numPackets) { QByteArray decodedBuffer; while (numPackets--) { + if (!_decoderMutex.tryLock()) { + // an incoming packet is being processed, + // and will likely be on the ring buffer shortly, + // so don't bother generating more data + qCInfo(audiostream, "Packet currently being unpacked or lost frame already being generated. Not generating lost frame."); + return 0; + } if (_decoder) { _decoder->lostFrame(decodedBuffer); } else { decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * _numChannels); memset(decodedBuffer.data(), 0, decodedBuffer.size()); } + _decoderMutex.unlock(); _ringBuffer.writeData(decodedBuffer.data(), decodedBuffer.size()); } return 0; @@ -259,6 +267,12 @@ int InboundAudioStream::lostAudioData(int numPackets) { int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) { QByteArray decodedBuffer; + + // may block on the real-time thread, which is acceptible as + // parseAudioData is only called by the packet processing + // thread which, while high performance, is not as sensitive to + // delays as the real-time thread. + QMutexLocker lock(&_decoderMutex); if (_decoder) { _decoder->decode(packetAfterStreamProperties, decodedBuffer); } else { @@ -277,16 +291,23 @@ int InboundAudioStream::writeDroppableSilentFrames(int silentFrames) { // case we will call the decoder's lostFrame() method, which indicates // that it should interpolate from its last known state down toward // silence. - if (_decoder) { - // FIXME - We could potentially use the output from the codec, in which - // case we might get a cleaner fade toward silence. NOTE: The below logic - // attempts to catch up in the event that the jitter buffers have grown. - // The better long term fix is to use the output from the decode, detect - // when it actually reaches silence, and then delete the silent portions - // of the jitter buffers. Or petentially do a cross fade from the decode - // output to silence. - QByteArray decodedBuffer; - _decoder->lostFrame(decodedBuffer); + { + // may block on the real-time thread, which is acceptible as + // writeDroppableSilentFrames is only called by the packet processing + // thread which, while high performance, is not as sensitive to + // delays as the real-time thread. + QMutexLocker lock(&_decoderMutex); + if (_decoder) { + // FIXME - We could potentially use the output from the codec, in which + // case we might get a cleaner fade toward silence. NOTE: The below logic + // attempts to catch up in the event that the jitter buffers have grown. + // The better long term fix is to use the output from the decode, detect + // when it actually reaches silence, and then delete the silent portions + // of the jitter buffers. Or petentially do a cross fade from the decode + // output to silence. + QByteArray decodedBuffer; + _decoder->lostFrame(decodedBuffer); + } } // calculate how many silent frames we should drop. @@ -344,7 +365,16 @@ int InboundAudioStream::popSamples(int maxSamples, bool allOrNothing) { //Kick PLC to generate a filler frame, reducing 'click' lostAudioData(allOrNothing ? (maxSamples - samplesAvailable) / _ringBuffer.getNumFrameSamples() : 1); samplesPopped = _ringBuffer.samplesAvailable(); - popSamplesNoCheck(samplesPopped); + if (samplesPopped) { + popSamplesNoCheck(samplesPopped); + } else { + // No samples available means a packet is currently being + // processed, so we don't generate lost audio data, and instead + // just wait for the packet to come in. This prevents locking + // the real-time audio thread at the cost of a potential (but rare) + // 'click' + _lastPopSucceeded = false; + } } } return samplesPopped; @@ -531,6 +561,7 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod _codec = codec; _selectedCodecName = codecName; if (_codec) { + QMutexLocker lock(&_decoderMutex); _decoder = codec->createDecoder(AudioConstants::SAMPLE_RATE, numChannels); } } @@ -538,6 +569,7 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod void InboundAudioStream::cleanupCodec() { // release any old codec encoder/decoder first... if (_codec) { + QMutexLocker lock(&_decoderMutex); if (_decoder) { _codec->releaseDecoder(_decoder); _decoder = nullptr; diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 5ff9e2c84c..c10a86cb69 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -187,6 +187,7 @@ protected: CodecPluginPointer _codec; QString _selectedCodecName; + QMutex _decoderMutex; Decoder* _decoder { nullptr }; int _mismatchedAudioCodecCount { 0 }; }; diff --git a/libraries/audio/src/MixedProcessedAudioStream.cpp b/libraries/audio/src/MixedProcessedAudioStream.cpp index 082977246b..ff2deac73e 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.cpp +++ b/libraries/audio/src/MixedProcessedAudioStream.cpp @@ -36,13 +36,20 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) { QByteArray outputBuffer; while (numPackets--) { + if (!_decoderMutex.tryLock()) { + // an incoming packet is being processed, + // and will likely be on the ring buffer shortly, + // so don't bother generating more data + qCInfo(audiostream, "Packet currently being unpacked or lost frame already being generated. Not generating lost frame."); + return 0; + } if (_decoder) { _decoder->lostFrame(decodedBuffer); } else { decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO); memset(decodedBuffer.data(), 0, decodedBuffer.size()); } - + _decoderMutex.unlock(); emit addedStereoSamples(decodedBuffer); emit processSamples(decodedBuffer, outputBuffer); @@ -55,6 +62,12 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) { int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) { QByteArray decodedBuffer; + + // may block on the real-time thread, which is acceptible as + // parseAudioData is only called by the packet processing + // thread which, while high performance, is not as sensitive to + // delays as the real-time thread. + QMutexLocker lock(&_decoderMutex); if (_decoder) { _decoder->decode(packetAfterStreamProperties, decodedBuffer); } else { From 3a4241b290cfe6fc9456e91a351d0de141467235 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 8 May 2019 16:31:00 -0700 Subject: [PATCH 51/83] Unnecessary spacing changes. --- libraries/audio/src/AudioSRC.cpp | 2 ++ libraries/audio/src/AudioSRC.h | 1 - libraries/audio/src/InboundAudioStream.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp index 867416871e..d488eccb6a 100644 --- a/libraries/audio/src/AudioSRC.cpp +++ b/libraries/audio/src/AudioSRC.cpp @@ -1491,6 +1491,7 @@ AudioSRC::~AudioSRC() { // int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { int outputFrames = 0; + while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); @@ -1515,6 +1516,7 @@ int AudioSRC::render(const int16_t* input, int16_t* output, int inputFrames) { // int AudioSRC::render(const float* input, float* output, int inputFrames) { int outputFrames = 0; + while (inputFrames) { int ni = MIN(inputFrames, _inputBlock); diff --git a/libraries/audio/src/AudioSRC.h b/libraries/audio/src/AudioSRC.h index 111c2105b7..d5971e5838 100644 --- a/libraries/audio/src/AudioSRC.h +++ b/libraries/audio/src/AudioSRC.h @@ -55,7 +55,6 @@ public: int getMaxInput(int outputFrames); private: - float* _polyphaseFilter; int* _stepTable; diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 482991626a..44f204c037 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -215,6 +215,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { if (framesAvailable > _desiredJitterBufferFrames + MAX_FRAMES_OVER_DESIRED) { int framesToDrop = framesAvailable - (_desiredJitterBufferFrames + DESIRED_JITTER_BUFFER_FRAMES_PADDING); _ringBuffer.shiftReadPosition(framesToDrop * _ringBuffer.getNumFrameSamples()); + _framesAvailableStat.reset(); _currentJitterBufferFrames = 0; From 0fae615cb4917fd2f2b9665df8c9f601a2dee74c Mon Sep 17 00:00:00 2001 From: danteruiz Date: Wed, 8 May 2019 17:42:22 -0700 Subject: [PATCH 52/83] fix dock widget context --- interface/src/Application.h | 3 ++- interface/src/ui/InteractiveWindow.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 300769b349..373e2c16a2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -211,6 +211,8 @@ public: float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second + static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); + float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -604,7 +606,6 @@ private: void maybeToggleMenuVisible(QMouseEvent* event) const; void toggleTabletUI(bool shouldOpen = false) const; - static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); void userKickConfirmation(const QUuid& nodeID); MainWindow* _window; diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index 3e0aee47c7..810d85fa89 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -92,6 +92,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap auto mainWindow = qApp->getWindow(); _dockWidget = std::shared_ptr(new DockWidget(title, mainWindow), dockWidgetDeleter); + auto quickView = _dockWidget->getQuickView(); + Application::setupQmlSurface(quickView->rootContext(), true); if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) { DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt(); @@ -119,7 +121,6 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap } } - auto quickView = _dockWidget->getQuickView(); QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) { if (status == QQuickView::Ready) { QQuickItem* rootItem = _dockWidget->getRootItem(); From 85f425a93b2d676c9dcc8c6d51e8675ab51ab241 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 May 2019 10:05:02 -0700 Subject: [PATCH 53/83] Apply variable output gain using cubic smoothing --- libraries/audio-client/src/AudioClient.cpp | 37 ++++++++++++++++++++++ libraries/audio-client/src/AudioClient.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4d3311b065..cef6adcab0 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -221,6 +221,35 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, return (float)loudness * scale; } +template +static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) { + + // fast path for unity gain + if (gain0 == 1.0f && gain1 == 1.0f) { + return; + } + + // cubic poly from gain0 to gain1 + float c3 = -2.0f * (gain1 - gain0); + float c2 = 3.0f * (gain1 - gain0); + float c0 = gain0; + + float t = 0.0f; + float tStep = 1.0f / numFrames; + + for (int i = 0; i < numFrames; i++) { + + // evaluate poly over t=[0,1) + float gain = (c3 * t + c2) * t * t + c0; + t += tStep; + + // apply gain to all channels + for (int ch = 0; ch < NUM_CHANNELS; ch++) { + buffer[NUM_CHANNELS*i + ch] *= gain; + } + } +} + static inline float convertToFloat(int16_t sample) { return (float)sample * (1 / 32768.0f); } @@ -2109,6 +2138,14 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int framesPopped = samplesPopped / AudioConstants::STEREO; int bytesWritten; if (samplesPopped > 0) { + + // apply output gain + float newGain = _audio->_outputGain.load(std::memory_order_acquire); + float oldGain = _audio->_lastOutputGain; + _audio->_lastOutputGain = newGain; + + applyGainSmoothing(mixBuffer, framesPopped, oldGain, newGain); + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { // limit the audio _audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7608bf5cdb..00dd7ae299 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -395,6 +395,8 @@ private: int _outputPeriod { 0 }; float* _outputMixBuffer { NULL }; int16_t* _outputScratchBuffer { NULL }; + std::atomic _outputGain { 1.0f }; + float _lastOutputGain { 1.0f }; // for local audio (used by audio injectors thread) std::atomic _localInjectorGain { 1.0f }; From b730c457caa9f5535b1b4b53ea1f9ef3cb199329 Mon Sep 17 00:00:00 2001 From: Saracen Date: Thu, 9 May 2019 18:08:36 +0100 Subject: [PATCH 54/83] Replace tabs with spaces --- scripts/system/away.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/system/away.js b/scripts/system/away.js index 2e4a5d507e..2d345c9821 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -285,10 +285,10 @@ function maybeGoAway() { if (Reticle.mouseCaptured !== wasMouseCaptured) { wasMouseCaptured = !wasMouseCaptured; if (!wasMouseCaptured) { - if (enterAwayStateWhenFocusLostInVR) { - goAway(); - return; - } + if (enterAwayStateWhenFocusLostInVR) { + goAway(); + return; + } } } @@ -354,13 +354,13 @@ eventMapping.from(Controller.Standard.Start).peek().to(goActive); Controller.enableMapping(eventMappingName); function awayStateWhenFocusLostInVRChanged(enabled) { - enterAwayStateWhenFocusLostInVR = enabled; + enterAwayStateWhenFocusLostInVR = enabled; } Script.scriptEnding.connect(function () { Script.clearInterval(maybeIntervalTimer); goActive(); - HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged); + HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged); Controller.disableMapping(eventMappingName); Controller.mousePressEvent.disconnect(goActive); Controller.keyPressEvent.disconnect(maybeGoActive); From 1b733d3fb3ed9063e81cb4b0876fec8976634f71 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 May 2019 12:56:07 -0700 Subject: [PATCH 55/83] While PTT is active, duck the audio output by 20dB to reduce echo --- interface/src/scripting/Audio.cpp | 2 ++ libraries/audio-client/src/AudioClient.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index b406c097e7..cb8b211352 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -366,8 +366,10 @@ void Audio::onContextChanged() { void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { + DependencyManager::get()->setOutputGain(0.1f); // duck the output by 20dB setMuted(false); } else { + DependencyManager::get()->setOutputGain(1.0f); setMuted(true); } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 00dd7ae299..db70e2f7b3 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -241,8 +241,10 @@ public slots: void setInputVolume(float volume, bool emitSignal = true); void setReverb(bool reverb); void setReverbOptions(const AudioEffectOptions* options); + void setLocalInjectorGain(float gain) { _localInjectorGain = gain; }; void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; }; + void setOutputGain(float gain) { _outputGain = gain; }; void outputNotify(); From c7432ffe5f483638ceb50794476873e21ff353c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 May 2019 13:48:07 -0700 Subject: [PATCH 56/83] also update easy flags when rebuilding avatar shape --- interface/src/avatar/AvatarManager.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ac74d0ab20..51fcaf0357 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -504,23 +504,18 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact } } else if (isInPhysics) { AvatarMotionState* motionState = avatar->_motionState; + uint32_t flags = motionState->getIncomingDirtyFlags(); + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + motionState->handleEasyChanges(flags); + } + // NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. + if (motionState->needsNewShape()) { rebuildAvatarPhysics(transaction, avatar); - } else { - uint32_t flags = motionState->getIncomingDirtyFlags(); + } else if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); motionState->clearIncomingDirtyFlags(); - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - motionState->handleEasyChanges(flags); - } - // NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC - // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. - - if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { - transaction.objectsToReinsert.push_back(motionState); - for (auto detailedMotionState : avatar->getDetailedMotionStates()) { - transaction.objectsToReinsert.push_back(detailedMotionState); - } - } } } } From c691ee496e7667f524592d68c702318a6562a3b0 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 9 May 2019 13:58:50 -0700 Subject: [PATCH 57/83] CR --- domain-server/src/DomainServer.cpp | 6 +++--- libraries/networking/src/DomainHandler.cpp | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 24e2a48c2d..051dd989f5 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1586,11 +1586,11 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply) { _sendICEServerAddressToMetaverseAPIInProgress = false; if (_sendICEServerAddressToMetaverseAPIRedo) { - qCDebug(domain_server_ice) << "ice-server address updated with metaverse, but has since changed. redoing update..."; + qCDebug(domain_server_ice) << "ice-server address (" << _iceServerSocket << ") updated with metaverse, but has since changed. redoing update..."; _sendICEServerAddressToMetaverseAPIRedo = false; sendICEServerAddressToMetaverseAPI(); } else { - qCDebug(domain_server_ice) << "ice-server address updated with metaverse."; + qCDebug(domain_server_ice) << "ice-server address (" << _iceServerSocket << ") updated with metaverse."; } } @@ -1603,7 +1603,7 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestRepl } else { const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000; - qCWarning(domain_server_ice) << "Failed to update ice-server address with High Fidelity Metaverse - error was" + qCWarning(domain_server_ice) << "Failed to update ice-server address (" << _iceServerSocket << ") with High Fidelity Metaverse - error was" << requestReply->errorString(); qCWarning(domain_server_ice) << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds"; diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 7793963959..37e1b84eda 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -562,10 +562,14 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer 1) { + qCDebug(networking_ice) << "Silent domain checkins:" << _checkInPacketsSinceLastReply; + } + if (_checkInPacketsSinceLastReply > MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that - qCDebug(networking) << "Limit of silent domain checkins reached"; + qCDebug(networking_ice) << "Limit of silent domain checkins reached"; emit limitOfSilentDomainCheckInsReached(); return true; } else { From ad36df04556cd39f8464d665dccab85b006d8f0f Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 9 May 2019 13:59:49 -0700 Subject: [PATCH 58/83] Fix build warnings --- CMakeLists.txt | 6 +++--- .../model-networking/src/model-networking/ModelCache.cpp | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49994d27b7..49d16ffa4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ endif() # Use default time server if none defined in environment set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp") -set(HIFI_USE_OPTIMIZED_IK OFF) +set(HIFI_USE_OPTIMIZED_IK_OPTION OFF) set(BUILD_CLIENT_OPTION ON) set(BUILD_SERVER_OPTION ON) set(BUILD_TESTS_OPTION OFF) @@ -126,7 +126,7 @@ if (USE_GLES AND (NOT ANDROID)) set(DISABLE_QML_OPTION ON) endif() -option(HIFI_USE_OPTIMIZED_IK "USE OPTIMIZED IK" ${HIFI_USE_OPTIMIZED_IK_OPTION}) +option(HIFI_USE_OPTIMIZED_IK "Use optimized IK" ${HIFI_USE_OPTIMIZED_IK_OPTION}) option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION}) option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION}) option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION}) @@ -157,7 +157,7 @@ foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") endforeach() -MESSAGE(STATUS "USE OPTIMIZED IK: " ${HIFI_USE_OPTIMIZED_IK}) +MESSAGE(STATUS "Use optimized IK: " ${HIFI_USE_OPTIMIZED_IK}) MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 26bd20d967..1ed1c65358 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -542,5 +542,3 @@ void GeometryResourceWatcher::resourceRefreshed() { // FIXME: Model is not set up to handle a refresh // _instance.reset(); } - -#include "ModelCache.moc" From 2bb522eea68dc7d91ca18212d766aa2fa1ea71a9 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 May 2019 14:16:59 -0700 Subject: [PATCH 59/83] entity icons ignorePickIntersection when not visible --- scripts/system/libraries/entityIconOverlayManager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index fc4612ae80..68104ff4bb 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -64,7 +64,8 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { visible = isVisible; for (var id in entityOverlays) { Overlays.editOverlay(entityOverlays[id], { - visible: visible + visible: visible, + ignorePickIntersection: !visible }); } } @@ -85,7 +86,8 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { function releaseOverlay(overlay) { unusedOverlays.push(overlay); Overlays.editOverlay(overlay, { - visible: false + visible: false, + ignorePickIntersection: true }); } @@ -99,6 +101,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { position: properties.position, rotation: Quat.fromPitchYawRollDegrees(0, 0, 270), visible: visible, + ignorePickIntersection: !visible, alpha: 0.9, scale: 0.5, drawInFront: true, From a0b34e4f15d04e442e719a7788b529dd11b39346 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 May 2019 14:45:36 -0700 Subject: [PATCH 60/83] more correct clearing of dirty flags for AvatarMotionState --- interface/src/avatar/AvatarManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 51fcaf0357..e01f9339f4 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -513,8 +513,10 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact if (motionState->needsNewShape()) { rebuildAvatarPhysics(transaction, avatar); - } else if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { - transaction.objectsToReinsert.push_back(motionState); + } else { + if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); + } motionState->clearIncomingDirtyFlags(); } } From 7a4e3557b0592accefd7369f14b6e548aa4d8122 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Thu, 9 May 2019 15:21:43 -0700 Subject: [PATCH 61/83] Use TryLocker instead of explicitly using tryLock on mutexes --- libraries/audio/src/InboundAudioStream.cpp | 5 +++-- libraries/audio/src/MixedProcessedAudioStream.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 44f204c037..5ac3996029 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -10,6 +10,7 @@ // #include "InboundAudioStream.h" +#include "TryLocker.h" #include @@ -247,7 +248,8 @@ int InboundAudioStream::lostAudioData(int numPackets) { QByteArray decodedBuffer; while (numPackets--) { - if (!_decoderMutex.tryLock()) { + MutexTryLocker lock(_decoderMutex); + if (!lock.isLocked()) { // an incoming packet is being processed, // and will likely be on the ring buffer shortly, // so don't bother generating more data @@ -260,7 +262,6 @@ int InboundAudioStream::lostAudioData(int numPackets) { decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * _numChannels); memset(decodedBuffer.data(), 0, decodedBuffer.size()); } - _decoderMutex.unlock(); _ringBuffer.writeData(decodedBuffer.data(), decodedBuffer.size()); } return 0; diff --git a/libraries/audio/src/MixedProcessedAudioStream.cpp b/libraries/audio/src/MixedProcessedAudioStream.cpp index ff2deac73e..6510f0bfc9 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.cpp +++ b/libraries/audio/src/MixedProcessedAudioStream.cpp @@ -11,6 +11,7 @@ #include "MixedProcessedAudioStream.h" #include "AudioLogging.h" +#include "TryLocker.h" MixedProcessedAudioStream::MixedProcessedAudioStream(int numFramesCapacity, int numStaticJitterFrames) : InboundAudioStream(AudioConstants::STEREO, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, @@ -36,7 +37,8 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) { QByteArray outputBuffer; while (numPackets--) { - if (!_decoderMutex.tryLock()) { + MutexTryLocker lock(_decoderMutex); + if (!lock.isLocked()) { // an incoming packet is being processed, // and will likely be on the ring buffer shortly, // so don't bother generating more data @@ -49,7 +51,6 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) { decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO); memset(decodedBuffer.data(), 0, decodedBuffer.size()); } - _decoderMutex.unlock(); emit addedStereoSamples(decodedBuffer); emit processSamples(decodedBuffer, outputBuffer); From 0a7352074ca29bbf9c3e6aa24e04ce7d987dbc74 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 9 May 2019 17:17:43 -0700 Subject: [PATCH 62/83] Fix android build --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index baa0e2037f..fc681cadc8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -9389,7 +9389,7 @@ void Application::showUrlHandler(const QUrl& url) { void Application::beforeEnterBackground() { auto nodeList = DependencyManager::get(); nodeList->setSendDomainServerCheckInEnabled(false); - nodeList->reset(true); + nodeList->reset("Entering background", true); clearDomainOctreeDetails(); } From 07372f102cbac94209ba02b35a571b19dce9b140 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 9 May 2019 17:19:10 -0700 Subject: [PATCH 63/83] Add logging on silent node removal --- libraries/networking/src/LimitedNodeList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index cc0ef8ae17..d87d74c188 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -885,6 +885,7 @@ void LimitedNodeList::removeSilentNodes() { }); foreach(const SharedNodePointer& killedNode, killedNodes) { + qCDebug(networking_ice) << "Removing silent node" << killedNode; handleNodeKill(killedNode); } } From db88f14e13bedb32543c60da69c8aab9d5fce102 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 May 2019 11:51:50 -0700 Subject: [PATCH 64/83] Error message instead of crash if the GL version is too low --- interface/src/main.cpp | 9 ++ libraries/gl/src/gl/GLHelpers.cpp | 153 ++++++++++++++++++++++++++++-- libraries/gl/src/gl/GLHelpers.h | 10 +- tests-manual/gl/src/main.cpp | 26 ++++- 4 files changed, 189 insertions(+), 9 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index b2be010544..11054d25d0 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -53,6 +53,15 @@ int main(int argc, const char* argv[]) { // https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg QSurfaceFormat::setDefaultFormat(format); #endif + +#if defined(Q_OS_WIN) + // Check the minimum version of + if (gl::getAvailableVersion() < gl::getRequiredVersion()) { + MessageBoxA(nullptr, "Interface requires OpenGL 4.1 or higher", "Unsupported", MB_OK); + return -1; + } +#endif + setupHifiApplication(BuildInfo::INTERFACE_NAME); QStringList arguments; diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 2c02fdca03..7b47736a3d 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -71,19 +71,159 @@ void gl::globalRelease(bool finish) {} #endif -void gl::getTargetVersion(int& major, int& minor) { +uint16_t gl::getTargetVersion() { + uint8_t major = 0, minor = 0; + #if defined(USE_GLES) major = 3; minor = 2; -#else -#if defined(Q_OS_MAC) +#elif defined(Q_OS_MAC) major = 4; minor = 1; #else major = 4; minor = disableGl45() ? 1 : 5; #endif + return GL_MAKE_VERSION(major, minor); +} + +uint16_t gl::getRequiredVersion() { + uint8_t major = 0, minor = 0; +#if defined(USE_GLES) + major = 3; + minor = 2; +#else + major = 4; + minor = 1; #endif + return GL_MAKE_VERSION(major, minor); +} + +#if defined(Q_OS_WIN) + +typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); +GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + +static bool setupPixelFormatSimple(HDC hdc) { + // FIXME build the PFD based on the + static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 24, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 1, // Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 24, // 24 Bit Z-Buffer (Depth Buffer) + 8, // 8 Bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + auto pixelFormat = ChoosePixelFormat(hdc, &pfd); + if (pixelFormat == 0) { + return false; + } + + if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) { + return false; + } + return true; +} + +#endif + +uint16_t gl::getAvailableVersion() { + static uint8_t major = 0, minor = 0; + static std::once_flag once; + std::call_once(once, [&] { +#if defined(USE_GLES) + // FIXME do runtime detection of the available GL version + major = 3; + minor = 2; +#elif defined(Q_OS_MAC) + // Damn it Apple. + major = 4; + minor = 1; +#elif defined(Q_OS_WIN) + // + HINSTANCE hInstance = GetModuleHandle(nullptr); + const auto windowClassName = "OpenGLVersionCheck"; + WNDCLASS wc = { }; + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = windowClassName; + RegisterClass(&wc); + + using Handle = std::shared_ptr; + HWND rawHwnd = CreateWindowEx( + WS_EX_APPWINDOW, // extended style + windowClassName, // class name + windowClassName, // title + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CS_OWNDC | WS_POPUP, // style + 0, 0, 10, 10, // position and size + NULL, NULL, hInstance, NULL); + auto WindowDestroyer = [](void* handle) { + DestroyWindow((HWND)handle); + }; + Handle hWnd = Handle(rawHwnd, WindowDestroyer); + if (!hWnd) { + return; + } + HDC rawDC = GetDC(rawHwnd); + auto DCDestroyer = [=](void* handle) { + ReleaseDC(rawHwnd, (HDC)handle); + }; + if (!rawDC) { + return; + } + Handle hDC = Handle(rawDC, DCDestroyer); + if (!setupPixelFormatSimple(rawDC)) { + return; + } + auto GLRCDestroyer = [](void* handle) { + wglDeleteContext((HGLRC)handle); + }; + auto rawglrc = wglCreateContext(rawDC); + if (!rawglrc) { + return; + } + Handle hGLRC = Handle(rawglrc, GLRCDestroyer); + if (!wglMakeCurrent(rawDC, rawglrc)) { + return; + } + gl::initModuleGl(); + wglMakeCurrent(0, 0); + hGLRC.reset(); + if (!wglChoosePixelFormatARB || !wglCreateContextAttribsARB) { + return; + } + + // The only two versions we care about on Windows + // are 4.5 and 4.1 + if (GLAD_GL_VERSION_4_5) { + major = 4; + minor = disableGl45() ? 1 : 5; + } else if (GLAD_GL_VERSION_4_1) { + major = 4; + minor = 1; + } +#else + // FIXME do runtime detection of GL version on non-Mac/Windows/Mobile platforms + major = 4; + minor = disableGl45() ? 1 : 5; +#endif + }); + return GL_MAKE_VERSION(major, minor); } const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { @@ -105,10 +245,9 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); - int major, minor; - ::gl::getTargetVersion(major, minor); - format.setMajorVersion(major); - format.setMinorVersion(minor); + auto glversion = ::gl::getTargetVersion(); + format.setMajorVersion(GL_GET_MAJOR_VERSION(glversion)); + format.setMinorVersion(GL_GET_MINOR_VERSION(glversion)); }); return format; } diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 2a798e6e98..95c3b6a5c2 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -34,6 +34,10 @@ int glVersionToInteger(QString glVersion); bool isRenderThread(); +#define GL_MAKE_VERSION(major, minor) ((major << 8) | minor) +#define GL_GET_MINOR_VERSION(glversion) (glversion & 0x00FF) +#define GL_GET_MAJOR_VERSION(glversion) ((glversion & 0xFF00) >> 8) + namespace gl { void globalLock(); void globalRelease(bool finish = true); @@ -52,7 +56,11 @@ namespace gl { bool disableGl45(); - void getTargetVersion(int& major, int& minor); + uint16_t getTargetVersion(); + + uint16_t getAvailableVersion(); + + uint16_t getRequiredVersion(); } // namespace gl #define CHECK_GL_ERROR() ::gl::checkGLErrorDebug(__FUNCTION__) diff --git a/tests-manual/gl/src/main.cpp b/tests-manual/gl/src/main.cpp index 7435c8c2bf..8eed70d7aa 100644 --- a/tests-manual/gl/src/main.cpp +++ b/tests-manual/gl/src/main.cpp @@ -10,17 +10,33 @@ #include #include #include +#include int main(int argc, char** argv) { + auto glversion = gl::getAvailableVersion(); + auto major = GL_GET_MAJOR_VERSION(glversion); + auto minor = GL_GET_MINOR_VERSION(glversion); + + if (glversion < GL_MAKE_VERSION(4, 1)) { + MessageBoxA(nullptr, "Interface requires OpenGL 4.1 or higher", "Unsupported", MB_OK); + return 0; + } QGuiApplication app(argc, argv); - + + bool quitting = false; + // FIXME need to handle window closing message so that we can stop the timer GLWindow* window = new GLWindow(); window->create(); window->show(); + window->setSurfaceType(QSurface::OpenGLSurface); + window->setFormat(getDefaultOpenGLSurfaceFormat()); bool contextCreated = false; QTimer* timer = new QTimer(); QObject::connect(timer, &QTimer::timeout, [&] { + if (quitting) { + return; + } if (!contextCreated) { window->createContext(); contextCreated = true; @@ -33,9 +49,17 @@ int main(int argc, char** argv) { window->swapBuffers(); window->doneCurrent(); }); + // FIXME need to handle window closing message so that we can stop the timer + QObject::connect(&app, &QCoreApplication::aboutToQuit, [&] { + quitting = true; + QObject::disconnect(timer, &QTimer::timeout, nullptr, nullptr); + timer->stop(); + timer->deleteLater(); + }); timer->setInterval(15); timer->setSingleShot(false); timer->start(); app.exec(); + return 0; } From 0f8ddae9df7c35c2a043ca8ceaf5ba421970b01e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 May 2019 12:01:07 -0700 Subject: [PATCH 65/83] Reduce spam from watchdog thread --- interface/src/Application.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ef1ab23e1b..b597a285aa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -331,6 +331,7 @@ static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; static const int ENTITY_SERVER_ADDED_TIMEOUT = 5000; static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000; +static const int WATCHDOG_TIMER_TIMEOUT = 100; static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled @@ -1138,6 +1139,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto deadlockWatchdogThread = new DeadlockWatchdogThread(); deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId()); deadlockWatchdogThread->start(); + + + // Main thread timer to keep the watchdog updated + QTimer* watchdogUpdateTimer = new QTimer(this); + connect(watchdogUpdateTimer, &QTimer::timeout, [this] { updateHeartbeat(); }); + connect(this, &QCoreApplication::aboutToQuit, [watchdogUpdateTimer] { + watchdogUpdateTimer->stop(); + watchdogUpdateTimer->deleteLater(); + }); + watchdogUpdateTimer->setSingleShot(false); + watchdogUpdateTimer->setInterval(WATCHDOG_TIMER_TIMEOUT); // 100ms, Qt::CoarseTimer acceptable + watchdogUpdateTimer->start(); } // Set File Logger Session UUID @@ -4990,9 +5003,6 @@ void setupCpuMonitorThread() { void Application::idle() { PerformanceTimer perfTimer("idle"); - // Update the deadlock watchdog - updateHeartbeat(); - #if !defined(DISABLE_QML) auto offscreenUi = getOffscreenUI(); From 4b51d81b422438d6c417fe3e831476c92779a3a8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 May 2019 12:06:38 -0700 Subject: [PATCH 66/83] Clear any reliable connection state upon Node ID change --- interface/src/avatar/MyAvatar.cpp | 1 + libraries/networking/src/NodeList.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e3fbbe12ad..39f2d9f332 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3755,6 +3755,7 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings void MyAvatar::leaveDomain() { clearScaleRestriction(); saveAvatarScale(); + prepareResetTraitInstances(); } void MyAvatar::saveAvatarScale() { diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0a4c63d712..d870c65c66 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -665,6 +665,8 @@ void NodeList::processDomainServerList(QSharedPointer message) // tell the domain handler that we're no longer connected so that below // it can re-perform actions as if we just connected _domainHandler.setIsConnected(false); + // Clear any reliable connections using old ID. + _nodeSocket.clearConnections(); } setSessionLocalID(newLocalID); From 442a679108fed02716655a043ad90d4683b38d46 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 10 May 2019 14:12:10 -0700 Subject: [PATCH 67/83] Make DepenencyManager thread-safe again Before this PR, there were no locks around the two internal QHash data structures. Races are rare, due to the fact that the DependencyManager is initialized on the main thread on startup and the fact that a static QWeakPointer was used as an internal cache. However, there have been reported crashes where the render thread uses DependencyManager::isSet() perhaps while the main thread is adding a late dependency. DependencyManager::isSet() did not use the static QWeakPointer cache and was more prone to race conditions. To avoid this and perhaps other data races, mutexes now guard both of the internal QHash data structures. Also, as an optimization, the most frequent call to DependencyManager::isSet was removed (Profile.cpp). --- libraries/shared/src/DependencyManager.cpp | 11 +- libraries/shared/src/DependencyManager.h | 32 +-- libraries/shared/src/Profile.cpp | 4 +- tests/shared/src/DependencyManagerTests.cpp | 218 ++++++++++++++++++++ tests/shared/src/DependencyManagerTests.h | 21 ++ 5 files changed, 268 insertions(+), 18 deletions(-) create mode 100644 tests/shared/src/DependencyManagerTests.cpp create mode 100644 tests/shared/src/DependencyManagerTests.h diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index a870feab98..b8fbcad490 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -21,6 +21,13 @@ DependencyManager& DependencyManager::manager() { return *instance; } -QSharedPointer& DependencyManager::safeGet(size_t hashCode) { - return _instanceHash[hashCode]; +QSharedPointer DependencyManager::safeGet(size_t hashCode) const { + QMutexLocker lock(&_instanceHashMutex); + return _instanceHash.value(hashCode); } + +void DependencyManager::safeSet(size_t hashCode, const QSharedPointer& value) { + QMutexLocker lock(&_instanceHashMutex); + _instanceHash.insert(hashCode, value); +} + diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index bda1077990..90b0888185 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -81,13 +82,17 @@ private: static DependencyManager& manager(); template - size_t getHashCode(); + size_t getHashCode() const; - QSharedPointer& safeGet(size_t hashCode); + QSharedPointer safeGet(size_t hashCode) const; + void safeSet(size_t hashCode, const QSharedPointer& value); QHash> _instanceHash; QHash _inheritanceHash; + mutable QMutex _instanceHashMutex; + mutable QMutex _inheritanceHashMutex; + bool _exiting { false }; }; @@ -121,19 +126,15 @@ template bool DependencyManager::isSet() { static size_t hashCode = manager().getHashCode(); - QSharedPointer& instance = manager().safeGet(hashCode); + QSharedPointer instance = manager().safeGet(hashCode); return !instance.isNull(); } template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); - - QSharedPointer& instance = manager().safeGet(hashCode); - instance.clear(); // Clear instance before creation of new one to avoid edge cases QSharedPointer newInstance(new T(args...), &T::customDeleter); - QSharedPointer storedInstance = qSharedPointerCast(newInstance); - instance.swap(storedInstance); + manager().safeSet(hashCode, newInstance); return newInstance; } @@ -141,12 +142,8 @@ QSharedPointer DependencyManager::set(Args&&... args) { template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); - - QSharedPointer& instance = manager().safeGet(hashCode); - instance.clear(); // Clear instance before creation of new one to avoid edge cases QSharedPointer newInstance(new I(args...), &I::customDeleter); - QSharedPointer storedInstance = qSharedPointerCast(newInstance); - instance.swap(storedInstance); + manager().safeSet(hashCode, newInstance); return newInstance; } @@ -154,9 +151,12 @@ QSharedPointer DependencyManager::set(Args&&... args) { template void DependencyManager::destroy() { static size_t hashCode = manager().getHashCode(); - QSharedPointer& shared = manager().safeGet(hashCode); + + QMutexLocker lock(&manager()._instanceHashMutex); + QSharedPointer shared = manager()._instanceHash.take(hashCode); QWeakPointer weak = shared; shared.clear(); + // Check that the dependency was actually destroyed. If it wasn't, it was improperly captured somewhere if (weak.lock()) { qWarning() << "DependencyManager::destroy():" << typeid(T).name() << "was not properly destroyed!"; @@ -167,12 +167,14 @@ template void DependencyManager::registerInheritance() { size_t baseHashCode = typeHash(); size_t derivedHashCode = typeHash(); + QMutexLocker lock(&manager()._inheritanceHashMutex); manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template -size_t DependencyManager::getHashCode() { +size_t DependencyManager::getHashCode() const { size_t hashCode = typeHash(); + QMutexLocker lock(&_inheritanceHashMutex); auto derivedHashCode = _inheritanceHash.find(hashCode); while (derivedHashCode != _inheritanceHash.end()) { diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 018636ad5a..1ad7b0785b 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -39,7 +39,9 @@ Q_LOGGING_CATEGORY(trace_baker, "trace.baker") #endif static bool tracingEnabled() { - return DependencyManager::isSet() && DependencyManager::get()->isEnabled(); + // Cheers, love! The cavalry's here! + auto tracer = DependencyManager::get(); + return (tracer && tracer->isEnabled()); } DurationBase::DurationBase(const QLoggingCategory& category, const QString& name) : _name(name), _category(category) { diff --git a/tests/shared/src/DependencyManagerTests.cpp b/tests/shared/src/DependencyManagerTests.cpp new file mode 100644 index 0000000000..6afca088a0 --- /dev/null +++ b/tests/shared/src/DependencyManagerTests.cpp @@ -0,0 +1,218 @@ + +// +// Created by Anthony Thibault on May 9th 2019 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "DependencyManagerTests.h" + +#include +#include +#include + +#include +#include + +// x macro +#define LIST_OF_CLASSES \ + CLASS(A) \ + CLASS(B) \ + CLASS(C) \ + CLASS(D) \ + CLASS(E) \ + CLASS(F) \ + CLASS(G) \ + CLASS(H) \ + CLASS(I) \ + CLASS(J) \ + CLASS(K) \ + CLASS(L) \ + CLASS(M) \ + CLASS(N) \ + CLASS(O) \ + CLASS(P) \ + CLASS(Q) \ + CLASS(R) \ + CLASS(S) \ + CLASS(T) \ + CLASS(U) \ + CLASS(V) \ + CLASS(W) \ + CLASS(X) \ + CLASS(Y) \ + CLASS(Z) \ + CLASS(AA) \ + CLASS(AB) \ + CLASS(AC) \ + CLASS(AD) \ + CLASS(AE) \ + CLASS(AF) \ + CLASS(AG) \ + CLASS(AH) \ + CLASS(AI) \ + CLASS(AJ) \ + CLASS(AK) \ + CLASS(AL) \ + CLASS(AM) \ + CLASS(AN) \ + CLASS(AO) \ + CLASS(AP) \ + CLASS(AQ) \ + CLASS(AR) \ + CLASS(AS) \ + CLASS(AT) \ + CLASS(AU) \ + CLASS(AV) \ + CLASS(AW) \ + CLASS(AX) \ + CLASS(AY) \ + CLASS(AZ) \ + CLASS(BA) \ + CLASS(BB) \ + CLASS(BC) \ + CLASS(BD) \ + CLASS(BE) \ + CLASS(BF) \ + CLASS(BG) \ + CLASS(BH) \ + CLASS(BI) \ + CLASS(BJ) \ + CLASS(BK) \ + CLASS(BL) \ + CLASS(BM) \ + CLASS(BN) \ + CLASS(BO) \ + CLASS(BP) \ + CLASS(BQ) \ + CLASS(BR) \ + CLASS(BS) \ + CLASS(BT) \ + CLASS(BU) \ + CLASS(BV) \ + CLASS(BW) \ + CLASS(BX) \ + CLASS(BY) \ + CLASS(BZ) \ + CLASS(CA) \ + CLASS(CB) \ + CLASS(CC) \ + CLASS(CD) \ + CLASS(CE) \ + CLASS(CF) \ + CLASS(CG) \ + CLASS(CH) \ + CLASS(CI) \ + CLASS(CJ) \ + CLASS(CK) \ + CLASS(CL) \ + CLASS(CM) \ + CLASS(CN) \ + CLASS(CO) \ + CLASS(CP) \ + CLASS(CQ) \ + CLASS(CR) \ + CLASS(CS) \ + CLASS(CT) \ + CLASS(CU) \ + CLASS(CV) \ + CLASS(CW) \ + CLASS(CX) \ + CLASS(CY) \ + CLASS(CZ) \ + CLASS(DA) \ + CLASS(DB) \ + CLASS(DC) \ + CLASS(DD) \ + CLASS(DE) \ + CLASS(DF) \ + CLASS(DG) \ + CLASS(DH) \ + CLASS(DI) \ + CLASS(DJ) \ + CLASS(DK) \ + CLASS(DL) \ + CLASS(DM) \ + CLASS(DN) \ + CLASS(DO) \ + CLASS(DP) \ + CLASS(DQ) \ + CLASS(DR) \ + CLASS(DS) \ + CLASS(DT) \ + CLASS(DU) \ + CLASS(DV) \ + CLASS(DW) \ + CLASS(DX) \ + CLASS(DY) \ + CLASS(DZ) + + +QTEST_MAIN(DependencyManagerTests) + +#define CLASS(NAME) class NAME : public Dependency {}; +LIST_OF_CLASSES +#undef CLASS + +void DependencyManagerTests::testDependencyManager() { + QCOMPARE(DependencyManager::isSet(), false); + DependencyManager::set(); + QCOMPARE(DependencyManager::isSet(), true); + DependencyManager::destroy(); + QCOMPARE(DependencyManager::isSet(), false); +} + +static void addDeps() { + +#define CLASS(NAME) DependencyManager::set(); +LIST_OF_CLASSES +#undef CLASS +} + +static void removeDeps() { +#define CLASS(NAME) DependencyManager::destroy(); +LIST_OF_CLASSES +#undef CLASS +} + + +static void spamIsSet() { + for (int i = 0; i < 1000; i++) { +#define CLASS(NAME) DependencyManager::isSet(); +LIST_OF_CLASSES +#undef CLASS + } +} + +static void spamGet() { + for (int i = 0; i < 10; i++) { +#define CLASS(NAME) DependencyManager::get(); +LIST_OF_CLASSES +#undef CLASS + } +} + + +void assertDeps(bool value) { +#define CLASS(NAME) QCOMPARE(DependencyManager::isSet(), value); +LIST_OF_CLASSES +#undef CLASS +} + +void DependencyManagerTests::testDependencyManagerMultiThreaded() { + + std::thread isSetThread1(spamIsSet); // spawn new thread that checks of dpendencies are present by calling isSet() + std::thread getThread1(spamGet); // spawn new thread that checks of dpendencies are present by calling get() + addDeps(); + isSetThread1.join(); + getThread1.join(); + assertDeps(true); + + std::thread isSetThread2(spamIsSet); // spawn new thread that checks of dpendencies are present by calling isSet() + std::thread getThread2(spamGet); // spawn new thread that checks of dpendencies are present by calling get() + removeDeps(); + isSetThread2.join(); + getThread2.join(); + assertDeps(false); +} diff --git a/tests/shared/src/DependencyManagerTests.h b/tests/shared/src/DependencyManagerTests.h new file mode 100644 index 0000000000..d497eda8bf --- /dev/null +++ b/tests/shared/src/DependencyManagerTests.h @@ -0,0 +1,21 @@ +// +// Created by Anthony Thibault on May 9th 2019 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DependencyManagerTests_h +#define hifi_DependencyManagerTests_h + +#include + +class DependencyManagerTests : public QObject { + Q_OBJECT +private slots: + void testDependencyManager(); + void testDependencyManagerMultiThreaded(); +}; + +#endif // hifi_DependencyManagerTests_h From 6f5bf01f2b36efd130b066792c3f27ad01714641 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 10 May 2019 14:33:25 -0700 Subject: [PATCH 68/83] Add sanity check and logging for possible avatar fade lambda race condition --- interface/src/avatar/AvatarManager.cpp | 20 ++++++++++++------- .../src/avatars-renderer/Avatar.cpp | 5 +++++ .../src/avatars-renderer/Avatar.h | 2 ++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 00e743312f..287aae29b3 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -536,14 +536,20 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar workload::SpacePointer space = _space; transaction.transitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() { - const render::ScenePointer& scene = qApp->getMain3DScene(); - render::Transaction transaction; - avatar->removeFromScene(avatar, scene, transaction); - scene->enqueueTransaction(transaction); + if (avatar->getLastFadeRequested() != render::Transition::Type::USER_LEAVE_DOMAIN) { + // The avatar is using another transition besides the fade-out transition, which means it is still in use. + // Deleting the avatar now could cause state issues, so abort deletion and show message. + qCWarning(interfaceapp) << "An ending fade-out animation wants to delete an avatar, but the avatar is still in use. Avatar deletion has aborted. (avatar ID: " << avatar->getSessionUUID() << ")"; + } else { + const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; + avatar->removeFromScene(avatar, scene, transaction); + scene->enqueueTransaction(transaction); - workload::Transaction workloadTransaction; - workloadTransaction.remove(avatar->getSpaceIndex()); - space->enqueueTransaction(workloadTransaction); + workload::Transaction workloadTransaction; + workloadTransaction.remove(avatar->getSpaceIndex()); + space->enqueueTransaction(workloadTransaction); + } }); scene->enqueueTransaction(transaction); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index cb0acd68cb..942a0df6cd 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -690,6 +690,11 @@ void Avatar::fade(render::Transaction& transaction, render::Transition::Type typ transaction.addTransitionToItem(itemId, type, _renderItemID); } } + _lastFadeRequested = type; +} + +render::Transition::Type Avatar::getLastFadeRequested() const { + return _lastFadeRequested; } void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index a196c018d2..b16cb2ada6 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -523,6 +523,7 @@ public: void fadeIn(render::ScenePointer scene); void fadeOut(render::Transaction& transaction, KillAvatarReason reason); + render::Transition::Type getLastFadeRequested() const; // JSDoc is in AvatarData.h. Q_INVOKABLE virtual float getEyeHeight() const override; @@ -701,6 +702,7 @@ protected: virtual void updatePalms(); render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; + render::Transition::Type _lastFadeRequested { render::Transition::Type::NONE }; // Used for sanity checking ThreadSafeValueCache _leftPalmPositionCache { glm::vec3() }; ThreadSafeValueCache _leftPalmRotationCache { glm::quat() }; From 736c7a97365cbb8cd176225f19b57092cc712b81 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 10 May 2019 15:18:52 -0700 Subject: [PATCH 69/83] Change logging from 'fade-out animation' to 'fade-out transition' --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 287aae29b3..a7c52a3136 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -539,7 +539,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar if (avatar->getLastFadeRequested() != render::Transition::Type::USER_LEAVE_DOMAIN) { // The avatar is using another transition besides the fade-out transition, which means it is still in use. // Deleting the avatar now could cause state issues, so abort deletion and show message. - qCWarning(interfaceapp) << "An ending fade-out animation wants to delete an avatar, but the avatar is still in use. Avatar deletion has aborted. (avatar ID: " << avatar->getSessionUUID() << ")"; + qCWarning(interfaceapp) << "An ending fade-out transition wants to delete an avatar, but the avatar is still in use. Avatar deletion has aborted. (avatar ID: " << avatar->getSessionUUID() << ")"; } else { const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; From dfdace8cdc049714895bae5bea55b07a2cdaf956 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 10 May 2019 14:52:26 -0700 Subject: [PATCH 70/83] fix zones for real this time --- .../src/EntityTreeRenderer.cpp | 72 ++++++++++--------- .../src/EntityTreeRenderer.h | 14 ++-- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fbf577755d..ae8473e544 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -591,7 +591,7 @@ void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSetgetVisible() && renderableIdForEntity(entity) != render::Item::INVALID_ITEM_ID) { - _layeredZones.emplace(std::dynamic_pointer_cast(entity)); + _layeredZones.emplace_back(std::dynamic_pointer_cast(entity)); } if ((!hasScript && isZone) || scriptHasLoaded) { @@ -600,6 +600,7 @@ void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet(getTree()->findEntityByEntityItemID(id))) { - _layeredZones.update(zone, _avatarPosition, this); - applyLayeredZones(); + if (_layeredZones.update(zone, _avatarPosition, this)) { + applyLayeredZones(); + } } } bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& sessionUUID) { bool zonesChanged = false; - auto it = c.begin(); - while (it != c.end()) { + auto it = begin(); + while (it != end()) { auto zone = it->zone.lock(); if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) { zonesChanged = true; - it = c.erase(it); + it = erase(it); } else { it++; } } if (zonesChanged) { - std::make_heap(c.begin(), c.end(), comp); + sort(); } return zonesChanged; } std::pair EntityTreeRenderer::LayeredZones::getZoneInteractionProperties() const { - auto it = c.cbegin(); - while (it != c.cend()) { + for (auto it = cbegin(); it != cend(); it++) { auto zone = it->zone.lock(); if (zone && zone->isDomainEntity()) { return { zone->getFlyingAllowed(), zone->getGhostingAllowed() }; } - it++; } return { true, true }; } -void EntityTreeRenderer::LayeredZones::remove(const std::shared_ptr& zone) { - auto it = c.begin(); - while (it != c.end()) { - if (it->zone.lock() == zone) { - break; - } - it++; - } - if (it != c.end()) { - c.erase(it); - std::make_heap(c.begin(), c.end(), comp); - } -} - -void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) { +bool EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) { // When a zone's position or visibility changes, we call this method // In order to resort our zones, we first remove the changed zone, and then re-insert it if necessary - remove(zone); + + bool needsResort = false; + + { + auto it = begin(); + while (it != end()) { + if (it->zone.lock() == zone) { + break; + } + it++; + } + if (it != end()) { + erase(it); + needsResort = true; + } + } // Only call contains if the zone is rendering if (zone->isVisible() && entityTreeRenderer->renderableIdForEntity(zone) != render::Item::INVALID_ITEM_ID && zone->contains(position)) { - emplace(zone); + emplace_back(zone); + needsResort = true; } + + if (needsResort) { + sort(); + } + + return needsResort; } bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const { @@ -1254,9 +1262,9 @@ bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const { return false; } - auto it = c.cbegin(); - auto otherIt = other.c.cbegin(); - while (it != c.cend()) { + auto it = cbegin(); + auto otherIt = other.cbegin(); + while (it != cend()) { if (*it != *otherIt) { return false; } @@ -1268,15 +1276,13 @@ bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const { } void EntityTreeRenderer::LayeredZones::appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const { - auto it = c.cbegin(); - while (it != c.cend()) { + for (auto it = cbegin(); it != cend(); it++) { if (it->zone.lock()) { auto id = entityTreeRenderer->renderableIdForEntityId(it->id); if (id != render::Item::INVALID_ITEM_ID) { list.push_back(id); } } - it++; } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index a0243a159a..f794d947ed 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -213,24 +213,24 @@ private: public: LayeredZone(std::shared_ptr zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {} - bool operator>(const LayeredZone& r) const { return volume > r.volume; } - bool operator==(const LayeredZone& r) const { return zone.lock() == r.zone.lock(); } + // We need to sort on volume AND id so that different clients sort zones with identical volumes the same way + bool operator<(const LayeredZone& r) const { return volume < r.volume || (volume == r.volume && id < r.id); } + bool operator==(const LayeredZone& r) const { return zone.lock() && zone.lock() == r.zone.lock(); } bool operator!=(const LayeredZone& r) const { return !(*this == r); } - bool operator>=(const LayeredZone& r) const { return (*this > r) || (*this == r); } + bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); } std::weak_ptr zone; QUuid id; float volume; }; - class LayeredZones : public std::priority_queue, std::greater> { + class LayeredZones : public std::vector { public: - void clear() { *this = LayeredZones(); } bool clearDomainAndNonOwnedZones(const QUuid& sessionUUID); + void sort() { std::sort(begin(), end(), std::less()); } bool equals(const LayeredZones& other) const; - void remove(const std::shared_ptr& zone); - void update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer); + bool update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer); void appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const; std::pair getZoneInteractionProperties() const; From fe17550a80486b901582780b54a3b202c2f6cd2a Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 10 May 2019 15:41:52 -0700 Subject: [PATCH 71/83] Code review feedback In DependencyManager::set<>() destroy the clear the previous before allocating the new instance. --- libraries/shared/src/DependencyManager.cpp | 5 ----- libraries/shared/src/DependencyManager.h | 21 ++++++++++++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index b8fbcad490..e56dd0df88 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -26,8 +26,3 @@ QSharedPointer DependencyManager::safeGet(size_t hashCode) const { return _instanceHash.value(hashCode); } -void DependencyManager::safeSet(size_t hashCode, const QSharedPointer& value) { - QMutexLocker lock(&_instanceHashMutex); - _instanceHash.insert(hashCode, value); -} - diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 90b0888185..e188c8e9a4 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -85,7 +85,6 @@ private: size_t getHashCode() const; QSharedPointer safeGet(size_t hashCode) const; - void safeSet(size_t hashCode, const QSharedPointer& value); QHash> _instanceHash; QHash _inheritanceHash; @@ -133,8 +132,16 @@ bool DependencyManager::isSet() { template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); + QMutexLocker lock(&manager()._instanceHashMutex); + + // clear the previous instance before constructing the new instance + auto iter = manager()._instanceHash.find(hashCode); + if (iter != manager()._instanceHash.end()) { + iter.value().clear(); + } + QSharedPointer newInstance(new T(args...), &T::customDeleter); - manager().safeSet(hashCode, newInstance); + manager()._instanceHash.insert(hashCode, newInstance); return newInstance; } @@ -142,8 +149,16 @@ QSharedPointer DependencyManager::set(Args&&... args) { template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); + QMutexLocker lock(&manager()._instanceHashMutex); + + // clear the previous instance before constructing the new instance + auto iter = manager()._instanceHash.find(hashCode); + if (iter != manager()._instanceHash.end()) { + iter.value().clear(); + } + QSharedPointer newInstance(new I(args...), &I::customDeleter); - manager().safeSet(hashCode, newInstance); + _instanceHash.insert(hashCode, newInstance); return newInstance; } From b46378e0b67801ba5a25b283f14c3ac6ba46a3cd Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 10 May 2019 15:51:42 -0700 Subject: [PATCH 72/83] Compile error fix --- libraries/shared/src/DependencyManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index e188c8e9a4..8eddb572cf 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -158,7 +158,7 @@ QSharedPointer DependencyManager::set(Args&&... args) { } QSharedPointer newInstance(new I(args...), &I::customDeleter); - _instanceHash.insert(hashCode, newInstance); + manager()._instanceHash.insert(hashCode, newInstance); return newInstance; } From eb8c7220801d541bd2e38573d1e053232827712a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 May 2019 14:11:29 -0700 Subject: [PATCH 73/83] fix runtime switch --- interface/src/Application.cpp | 2 +- interface/src/graphics/WorldBox.cpp | 2 +- interface/src/raypick/ParabolaPointer.cpp | 46 ++++----- interface/src/raypick/ParabolaPointer.h | 5 +- .../src/workload/GameWorkloadRenderer.cpp | 2 + .../src/avatars-renderer/Avatar.cpp | 12 +-- .../src/avatars-renderer/Avatar.h | 2 +- .../src/RenderableGizmoEntityItem.cpp | 4 +- .../src/RenderableGridEntityItem.cpp | 4 +- .../src/RenderableLineEntityItem.cpp | 3 +- .../src/RenderableModelEntityItem.cpp | 2 +- .../src/RenderablePolyLineEntityItem.cpp | 38 +++----- .../src/RenderablePolyLineEntityItem.h | 5 +- .../src/RenderableShapeEntityItem.cpp | 2 +- .../src/RenderableTextEntityItem.cpp | 10 +- .../procedural/src/procedural/Procedural.cpp | 1 + libraries/render-utils/src/GeometryCache.cpp | 94 +++++++++---------- libraries/render-utils/src/GeometryCache.h | 16 ++-- libraries/render-utils/src/Model.cpp | 4 +- libraries/render-utils/src/Model.h | 2 +- libraries/render-utils/src/RenderCommonTask.h | 12 +++ .../render-utils/src/RenderForwardTask.cpp | 2 + libraries/render-utils/src/RenderViewTask.cpp | 15 +-- libraries/render-utils/src/TextRenderer3D.cpp | 4 +- libraries/render-utils/src/TextRenderer3D.h | 4 +- libraries/render-utils/src/forward_grid.slf | 40 ++++++++ .../src/forward_grid_translucent.slf | 41 ++++++++ .../render-utils/src/forward_parabola.slf | 18 ++++ .../render-utils/src/parabola_forward.slv | 7 -- .../src/render-utils/forward_grid.slp | 1 + .../render-utils/forward_grid_translucent.slp | 1 + .../src/render-utils/forward_parabola.slp | 1 + libraries/render-utils/src/text/Font.cpp | 36 +++---- libraries/render-utils/src/text/Font.h | 14 ++- libraries/render/src/render/Args.h | 4 + libraries/shared/src/DisableDeferred.h | 24 ----- libraries/shared/src/RenderForward.h | 24 +++++ libraries/task/src/task/Task.h | 10 +- 38 files changed, 303 insertions(+), 211 deletions(-) create mode 100644 libraries/render-utils/src/forward_grid.slf create mode 100644 libraries/render-utils/src/forward_grid_translucent.slf create mode 100644 libraries/render-utils/src/forward_parabola.slf delete mode 100644 libraries/render-utils/src/parabola_forward.slv create mode 100644 libraries/render-utils/src/render-utils/forward_grid.slp create mode 100644 libraries/render-utils/src/render-utils/forward_grid_translucent.slp create mode 100644 libraries/render-utils/src/render-utils/forward_parabola.slp delete mode 100644 libraries/shared/src/DisableDeferred.h create mode 100644 libraries/shared/src/RenderForward.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6430792dc4..941b8543a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6722,7 +6722,7 @@ void Application::updateRenderArgs(float deltaTime) { } appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE); appRenderArgs._renderArgs._scene = getMain3DScene(); { diff --git a/interface/src/graphics/WorldBox.cpp b/interface/src/graphics/WorldBox.cpp index 908055e9c6..a28850207f 100644 --- a/interface/src/graphics/WorldBox.cpp +++ b/interface/src/graphics/WorldBox.cpp @@ -22,7 +22,7 @@ namespace render { PerformanceTimer perfTimer("worldBox"); auto& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, false, false, true, args->_renderMethod == Args::RenderMethod::FORWARD); WorldBoxRenderData::renderWorldBox(args, batch); } } diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 389f6ed286..23fc1cb4bd 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -20,8 +20,7 @@ const float ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_W const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA { false }; const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_DRAWINFRONT { false }; -gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_parabolaPipeline { nullptr }; -gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_transparentParabolaPipeline { nullptr }; +std::map, gpu::PipelinePointer> ParabolaPointer::RenderState::ParabolaRenderItem::_parabolaPipelines; ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, @@ -401,33 +400,34 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() { _bound = AABox(min, max - min); } -const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() { - if (!_parabolaPipeline || !_transparentParabolaPipeline) { - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola); - auto state = std::make_shared(); +gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline(bool forward) const { + if (_parabolaPipelines.empty()) { + using namespace shader::render_utils::program; + + static const std::vector> keys = { + std::make_tuple(false, false, parabola), std::make_tuple(false, true, forward_parabola), std::make_tuple(true, false, parabola_translucent)/*, std::make_tuple(true, true, forward_parabola_translucent)*/ + }; + + for (auto& key : keys) { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, + if (std::get<0>(key)) { + PrepareStencil::testMask(*state); + } else { + PrepareStencil::testMaskDrawShape(*state); + } + state->setBlendFunction(std::get<0>(key), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMaskDrawShape(*state); state->setCullMode(gpu::State::CULL_NONE); - _parabolaPipeline = gpu::Pipeline::create(program, state); + + _parabolaPipelines[{std::get<0>(key), std::get<1>(key)}] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state); } - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola_translucent); - auto state = std::make_shared(); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMask(*state); - state->setCullMode(gpu::State::CULL_NONE); - _transparentParabolaPipeline = gpu::Pipeline::create(program, state); - } + // The forward opaque/translucent pipelines are the same for now + _parabolaPipelines[{ true, true }] = _parabolaPipelines[{ false, true}]; } - return (_parabolaData.color.a < 1.0f ? _transparentParabolaPipeline : _parabolaPipeline); + return _parabolaPipelines[{ _parabolaData.color.a < 1.0f, forward }]; } void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args) { @@ -441,7 +441,7 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args) transform.setTranslation(_origin); batch.setModelTransform(transform); - batch.setPipeline(getParabolaPipeline()); + batch.setPipeline(getParabolaPipeline(args->_renderMethod == render::Args::RenderMethod::FORWARD)); const int MAX_SECTIONS = 100; if (glm::length2(_parabolaData.acceleration) < EPSILON) { diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index d4b705a7d2..94470971e6 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -26,9 +26,8 @@ public: bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled); ~ParabolaRenderItem() {} - static gpu::PipelinePointer _parabolaPipeline; - static gpu::PipelinePointer _transparentParabolaPipeline; - const gpu::PipelinePointer getParabolaPipeline(); + static std::map, gpu::PipelinePointer> _parabolaPipelines; + gpu::PipelinePointer getParabolaPipeline(bool forward) const; void render(RenderArgs* args); render::Item::Bound& editBound() { return _bound; } diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index a2fb32d396..2bb73999f1 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -145,6 +145,7 @@ void GameWorkloadRenderItem::setAllViews(const workload::Views& views) { } const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() { + // FIXME: this needs a forward pipeline, or to only write to one output if (!_drawAllProxiesPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadProxy); auto state = std::make_shared(); @@ -162,6 +163,7 @@ const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() { const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() { + // FIXME: this needs a forward pipeline, or to only write to one output if (!_drawAllViewsPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadView); auto state = std::make_shared(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index cb0acd68cb..4a106d0f36 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -779,7 +779,7 @@ void Avatar::render(RenderArgs* renderArgs) { pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch.setModelTransform(pointerTransform); - geometryCache->bindSimpleProgram(batch); + geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _leftPointerGeometryID); } } @@ -803,7 +803,7 @@ void Avatar::render(RenderArgs* renderArgs) { pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch.setModelTransform(pointerTransform); - geometryCache->bindSimpleProgram(batch); + geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _rightPointerGeometryID); } } @@ -829,7 +829,7 @@ void Avatar::render(RenderArgs* renderArgs) { auto& frustum = renderArgs->getViewFrustum(); auto textPosition = getDisplayNamePosition(); if (frustum.pointIntersectsFrustum(textPosition)) { - renderDisplayName(batch, frustum, textPosition); + renderDisplayName(batch, frustum, textPosition, renderArgs->_renderMethod == render::Args::FORWARD); } } } @@ -1034,7 +1034,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const g return result; } -void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const { +void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition, bool forward) const { PROFILE_RANGE_BATCH(batch, __FUNCTION__); bool shouldShowReceiveStats = showReceiveStats && !isMyAvatar(); @@ -1090,7 +1090,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); - DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true, true, forward); DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor, _nameRectGeometryID); } @@ -1103,7 +1103,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const batch.setModelTransform(textTransform); { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); - renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); + renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), forward); } } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index a196c018d2..53f13ef191 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -694,7 +694,7 @@ protected: glm::vec3 getDisplayNamePosition() const; Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const; - void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const; + void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition, bool forward) const; virtual bool shouldRenderHead(const RenderArgs* renderArgs) const; virtual void fixupModelsInScene(const render::ScenePointer& scene); diff --git a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp index 1a188ca163..9468fdf3ef 100644 --- a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp @@ -261,15 +261,17 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) { Transform transform; bool hasTickMarks; glm::vec4 tickProperties; + bool forward; withReadLock([&] { transform = _renderTransform; hasTickMarks = _ringProperties.getHasTickMarks(); tickProperties = glm::vec4(_ringProperties.getMajorTickMarksAngle(), _ringProperties.getMajorTickMarksLength(), _ringProperties.getMinorTickMarksAngle(), _ringProperties.getMinorTickMarksLength()); + forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD; }); bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES; - geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, _renderLayer != RenderLayer::WORLD); + geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, forward); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp index f05ba35c79..31969e36fc 100644 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp @@ -113,11 +113,13 @@ void GridEntityRenderer::doRender(RenderArgs* args) { glm::vec4 color; glm::vec3 dimensions; Transform renderTransform; + bool forward; withReadLock([&] { color = glm::vec4(toGlm(_color), _alpha); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); dimensions = _dimensions; renderTransform = _renderTransform; + forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD; }); if (!_visible) { @@ -153,5 +155,5 @@ void GridEntityRenderer::doRender(RenderArgs* args) { DependencyManager::get()->renderGrid(*batch, minCorner, maxCorner, minorGridRowDivisions, minorGridColDivisions, MINOR_GRID_EDGE, majorGridRowDivisions, majorGridColDivisions, MAJOR_GRID_EDGE, - color, _geometryId); + color, forward, _geometryId); } \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 9c5424950a..cbe2a98166 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -55,7 +55,8 @@ void LineEntityRenderer::doRender(RenderArgs* args) { transform.setRotation(modelTransform.getRotation()); batch.setModelTransform(transform); if (_linePoints.size() > 1) { - DependencyManager::get()->bindSimpleProgram(batch); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, false, false, true, + _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD); DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bfbbe12ea6..df5b1d91f0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1520,7 +1520,7 @@ void ModelEntityRenderer::doRender(RenderArgs* args) { model = _model; }); if (model) { - model->renderDebugMeshBoxes(batch); + model->renderDebugMeshBoxes(batch, args->_renderMethod == Args::RenderMethod::FORWARD); } #endif } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 2430643ce2..d7246f9ba5 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -19,15 +19,12 @@ #include #include -#include - #include "paintStroke_Shared.slh" using namespace render; using namespace render::entities; -gpu::PipelinePointer PolyLineEntityRenderer::_pipeline = nullptr; -gpu::PipelinePointer PolyLineEntityRenderer::_glowPipeline = nullptr; +std::map, gpu::PipelinePointer> PolyLineEntityRenderer::_pipelines; static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png"); @@ -44,29 +41,24 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) } } -void PolyLineEntityRenderer::buildPipeline() { - // FIXME: opaque pipeline - gpu::ShaderPointer program = gpu::Shader::createProgram(DISABLE_DEFERRED ? shader::entities_renderer::program::paintStroke_forward : shader::entities_renderer::program::paintStroke); +void PolyLineEntityRenderer::buildPipelines() { + // FIXME: opaque pipelines + + static const std::vector> keys = { + { render::Args::DEFERRED, false }, { render::Args::DEFERRED, true }, { render::Args::FORWARD, false }, { render::Args::FORWARD, true }, + }; + + for (auto& key : keys) { + gpu::ShaderPointer program = gpu::Shader::createProgram(key.first == render::Args::DEFERRED ? shader::entities_renderer::program::paintStroke : shader::entities_renderer::program::paintStroke_forward); - { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setCullMode(gpu::State::CullMode::CULL_NONE); - state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setDepthTest(true, !key.second, gpu::LESS_EQUAL); PrepareStencil::testMask(*state); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _pipeline = gpu::Pipeline::create(program, state); - } - { - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setCullMode(gpu::State::CullMode::CULL_NONE); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - PrepareStencil::testMask(*state); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _glowPipeline = gpu::Pipeline::create(program, state); + _pipelines[key] = gpu::Pipeline::create(program, state); } } @@ -299,11 +291,11 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { return; } - if (!_pipeline) { - buildPipeline(); + if (_pipelines.empty()) { + buildPipelines(); } - batch.setPipeline(_glow ? _glowPipeline : _pipeline); + batch.setPipeline(_pipelines[{args->_renderMethod, _glow}]); batch.setModelTransform(transform); batch.setResourceTexture(0, texture); batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 3815b57671..adb30362da 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -37,7 +37,7 @@ protected: virtual ShapeKey getShapeKey() override; virtual void doRender(RenderArgs* args) override; - void buildPipeline(); + static void buildPipelines(); void updateGeometry(); void updateData(); @@ -58,8 +58,7 @@ protected: size_t _numVertices; gpu::BufferPointer _polylineDataBuffer; gpu::BufferPointer _polylineGeometryBuffer; - static gpu::PipelinePointer _pipeline; - static gpu::PipelinePointer _glowPipeline; + static std::map, gpu::PipelinePointer> _pipelines; }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 2548ae5914..6a0d7b001c 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -280,7 +280,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; render::ShapePipelinePointer pipeline; - if (renderLayer == RenderLayer::WORLD) { + if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) { pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); } else { pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 81f367a956..a281c1d097 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -163,7 +163,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { Transform modelTransform; glm::vec3 dimensions; BillboardMode billboardMode; - bool layered; + bool forward; withReadLock([&] { modelTransform = _renderTransform; dimensions = _dimensions; @@ -174,7 +174,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created); backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha); backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created); - layered = _renderLayer != RenderLayer::WORLD; + forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; }); // Render background @@ -187,7 +187,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; // FIXME: we need to find a better way of rendering text so we don't have to do this - if (layered) { + if (forward) { DependencyManager::get()->setupKeyLightBatch(args, batch); } @@ -199,7 +199,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { if (backgroundColor.a > 0.0f) { batch.setModelTransform(transformToTopLeft); auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, layered); + geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, forward); geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); } @@ -210,7 +210,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin)); - _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, layered); + _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, forward); } } diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 9940da0b9a..dbdf9cc7d1 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -288,6 +288,7 @@ void Procedural::prepare(gpu::Batch& batch, recompiledShader = true; } + // FIXME: need to handle forward rendering batch.setPipeline(recompiledShader ? _proceduralPipelines[key] : pipeline->second); if (_shaderDirty || _uniformsDirty) { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2e762a0107..7bd6f88d71 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -37,8 +37,6 @@ #include "DeferredLightingEffect.h" -#include - namespace gr { using graphics::slot::texture::Texture; using graphics::slot::buffer::Buffer; @@ -115,6 +113,8 @@ static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, no static const uint SHAPE_TEXCOORD0_OFFSET = offsetof(GeometryCache::ShapeVertex, uv); static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, tangent); +std::map, gpu::PipelinePointer> GeometryCache::_gridPipelines; + void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { auto geometryCache = DependencyManager::get(); @@ -714,11 +714,13 @@ QHash GeometryCache::_simplePrograms; gpu::ShaderPointer GeometryCache::_simpleShader; gpu::ShaderPointer GeometryCache::_transparentShader; gpu::ShaderPointer GeometryCache::_unlitShader; +gpu::ShaderPointer GeometryCache::_simpleFadeShader; +gpu::ShaderPointer GeometryCache::_unlitFadeShader; gpu::ShaderPointer GeometryCache::_forwardSimpleShader; gpu::ShaderPointer GeometryCache::_forwardTransparentShader; gpu::ShaderPointer GeometryCache::_forwardUnlitShader; -gpu::ShaderPointer GeometryCache::_simpleFadeShader; -gpu::ShaderPointer GeometryCache::_unlitFadeShader; +gpu::ShaderPointer GeometryCache::_forwardSimpleFadeShader; +gpu::ShaderPointer GeometryCache::_forwardUnlitFadeShader; render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline; render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline; @@ -740,16 +742,13 @@ render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::S if (key.isFaded()) { if (key.isTranslucent()) { return _simpleTransparentFadePipeline; - } - else { + } else { return _simpleOpaqueFadePipeline; } - } - else { + } else { if (key.isTranslucent()) { return _simpleTransparentPipeline; - } - else { + } else { return _simpleOpaquePipeline; } } @@ -805,6 +804,8 @@ void GeometryCache::initializeShapePipelines() { _simpleTransparentPipeline = getShapePipeline(false, true, true, false); _forwardSimpleOpaquePipeline = getShapePipeline(false, false, true, false, false, true); _forwardSimpleTransparentPipeline = getShapePipeline(false, true, true, false, false, true); + + // FIXME: these need forward pipelines _simpleOpaqueFadePipeline = getFadingShapePipeline(false, false, false, false, false); _simpleTransparentFadePipeline = getFadingShapePipeline(false, true, false, false, false); _simpleWirePipeline = getShapePipeline(false, false, true, true); @@ -823,11 +824,11 @@ render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool } render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured, bool transparent, bool culled, - bool unlit, bool depthBias) { + bool unlit, bool depthBias, bool forward) { auto fadeEffect = DependencyManager::get(); auto fadeBatchSetter = fadeEffect->getBatchSetter(); auto fadeItemSetter = fadeEffect->getItemUniformSetter(); - return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, true), nullptr, + return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, true, true, forward), nullptr, [fadeBatchSetter, fadeItemSetter](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); fadeBatchSetter(shapePipeline, batch, args); @@ -937,7 +938,7 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color) void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, - const glm::vec4& color, int id) { + const glm::vec4& color, bool forward, int id) { Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge); Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge); Vec2FloatPairPair key(majorKey, minorKey); @@ -970,7 +971,7 @@ void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, co } // Set the grid pipeline - useGridPipeline(batch, gridBuffer, color.a < 1.0f); + useGridPipeline(batch, gridBuffer, color.a < 1.0f, forward); static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f); static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f); @@ -2038,39 +2039,34 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { } } -void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent) { - if (!_gridPipelineOpaque || !_gridPipelineTransparent) { +void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent, bool forward) { + if (_gridPipelines.empty()) { + using namespace shader::render_utils::program; const float DEPTH_BIAS = 0.001f; - // FIXME: need forward pipelines - { - auto program = gpu::Shader::createProgram(shader::render_utils::program::grid); - auto state = std::make_shared(); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMaskDrawShape(*state); - state->setCullMode(gpu::State::CULL_NONE); - state->setDepthBias(DEPTH_BIAS); - _gridPipelineOpaque = gpu::Pipeline::create(program, state); - } + static const std::vector> keys = { + std::make_tuple(false, false, grid), std::make_tuple(false, true, forward_grid), std::make_tuple(true, false, grid_translucent), std::make_tuple(true, true, forward_grid_translucent) + }; - { - auto program = gpu::Shader::createProgram(shader::render_utils::program::grid_translucent); - auto state = std::make_shared(); + for (auto& key : keys) { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(true, + if (std::get<0>(key)) { + PrepareStencil::testMask(*state); + } else { + PrepareStencil::testMaskDrawShape(*state); + } + state->setBlendFunction(std::get<0>(key), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMask(*state); state->setCullMode(gpu::State::CULL_NONE); state->setDepthBias(DEPTH_BIAS); - _gridPipelineTransparent = gpu::Pipeline::create(program, state); + + _gridPipelines[{std::get<0>(key), std::get<1>(key)}] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state); } } - batch.setPipeline(transparent ? _gridPipelineTransparent : _gridPipelineOpaque); + batch.setPipeline(_gridPipelines[{ transparent, forward }]); batch.setUniformBuffer(0, gridBuffer); } @@ -2162,6 +2158,7 @@ void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { + // FIXME: need a forward pipeline for this buildWebShader(shader::render_utils::program::simple_opaque_web_browser, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline); buildWebShader(shader::render_utils::program::simple_transparent_web_browser, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline); }); @@ -2197,22 +2194,21 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp _forwardSimpleShader = gpu::Shader::createProgram(forward_simple_textured); _forwardTransparentShader = gpu::Shader::createProgram(forward_simple_textured_transparent); _forwardUnlitShader = gpu::Shader::createProgram(forward_simple_textured_unlit); - if (DISABLE_DEFERRED) { - _simpleShader = _forwardSimpleShader; - _transparentShader = _forwardTransparentShader; - _unlitShader = _forwardUnlitShader; - } else { - _simpleShader = gpu::Shader::createProgram(simple_textured); - _transparentShader = gpu::Shader::createProgram(simple_transparent_textured); - _unlitShader = gpu::Shader::createProgram(simple_textured_unlit); - } + + _simpleShader = gpu::Shader::createProgram(simple_textured); + _transparentShader = gpu::Shader::createProgram(simple_transparent_textured); + _unlitShader = gpu::Shader::createProgram(simple_textured_unlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; - _simpleFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured : simple_textured_fade); - _unlitFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured_unlit : simple_textured_unlit_fade); + // FIXME: these aren't right... + _forwardSimpleFadeShader = gpu::Shader::createProgram(forward_simple_textured); + _forwardUnlitFadeShader = gpu::Shader::createProgram(forward_simple_textured_unlit); + + _simpleFadeShader = gpu::Shader::createProgram(simple_textured_fade); + _unlitFadeShader = gpu::Shader::createProgram(simple_textured_unlit_fade); }); } @@ -2240,8 +2236,8 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp gpu::ShaderPointer program; if (config.isForward()) { - program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _forwardUnlitShader) : - (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _forwardTransparentShader : _forwardSimpleShader)); + program = (config.isUnlit()) ? (config.isFading() ? _forwardUnlitFadeShader : _forwardUnlitShader) : + (config.isFading() ? _forwardSimpleFadeShader : (config.isTransparent() ? _forwardTransparentShader : _forwardSimpleShader)); } else { program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _transparentShader : _simpleShader)); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 4ff061786a..a42b059a8c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -266,7 +266,7 @@ public: void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, - const glm::vec4& color, int id); + const glm::vec4& color, bool forward, int id); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id); @@ -400,9 +400,8 @@ private: glm::vec4 edge; }; using GridBuffer = gpu::BufferView; - void useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered); - gpu::PipelinePointer _gridPipelineOpaque; - gpu::PipelinePointer _gridPipelineTransparent; + void useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent, bool forward); + static std::map, gpu::PipelinePointer> _gridPipelines; class BatchItemDetails { public: @@ -460,11 +459,14 @@ private: static gpu::ShaderPointer _simpleShader; static gpu::ShaderPointer _transparentShader; static gpu::ShaderPointer _unlitShader; + static gpu::ShaderPointer _simpleFadeShader; + static gpu::ShaderPointer _unlitFadeShader; static gpu::ShaderPointer _forwardSimpleShader; static gpu::ShaderPointer _forwardTransparentShader; static gpu::ShaderPointer _forwardUnlitShader; - static gpu::ShaderPointer _simpleFadeShader; - static gpu::ShaderPointer _unlitFadeShader; + static gpu::ShaderPointer _forwardSimpleFadeShader; + static gpu::ShaderPointer _forwardUnlitFadeShader; + static render::ShapePipelinePointer _simpleOpaquePipeline; static render::ShapePipelinePointer _simpleTransparentPipeline; static render::ShapePipelinePointer _forwardSimpleOpaquePipeline; @@ -483,7 +485,7 @@ private: static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool forward = false); static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false); + bool unlit = false, bool depthBias = false, bool forward = false); }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7d0119a0f2..003a95e46d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1033,7 +1033,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti _renderInfoHasTransparent = false; } -void Model::renderDebugMeshBoxes(gpu::Batch& batch) { +void Model::renderDebugMeshBoxes(gpu::Batch& batch, bool forward) { int colorNdx = 0; _mutex.lock(); @@ -1042,7 +1042,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { Transform meshToWorld(meshToWorldMatrix); batch.setModelTransform(meshToWorld); - DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true); + DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true, forward); for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { for (auto &partTriangleSet : meshTriangleSets) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 7844e9bc41..7c861a3bc1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -347,7 +347,7 @@ public: const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; } - void renderDebugMeshBoxes(gpu::Batch& batch); + void renderDebugMeshBoxes(gpu::Batch& batch, bool forward); int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index b43a10aa7b..4f72600d34 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -137,4 +137,16 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); }; +class SetRenderMethod { +public: + using JobModel = render::Job::Model; + + SetRenderMethod(render::Args::RenderMethod method) : _method(method) {} + + void run(const render::RenderContextPointer& renderContext) { renderContext->args->_renderMethod = _method; } + +protected: + render::Args::RenderMethod _method; +}; + #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 5e30308a05..de55f3f4ff 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -48,6 +48,8 @@ using namespace render; extern void initForwardPipelines(ShapePlumber& plumber); void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + task.addJob("SetRenderMethodTask", render::Args::FORWARD); + // Prepare the ShapePipelines auto fadeEffect = DependencyManager::get(); ShapePlumberPointer shapePlumber = std::make_shared(); diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index a38769165b..ffaecedb0b 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -11,12 +11,15 @@ #include "RenderViewTask.h" #include "RenderShadowTask.h" +#include "RenderCommonTask.h" #include "RenderDeferredTask.h" #include "RenderForwardTask.h" -#include +#include void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { + task.addJob("SetRenderMethodTask", render::Args::DEFERRED); + const auto items = input.getN(0); const auto lightingModel = input.getN(1); const auto lightingStageFramesAndZones = input.getN(2); @@ -31,9 +34,9 @@ void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& } void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { - task.addBranch("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask); + task.addBranch("RenderShadowsAndDeferredTask", RENDER_FORWARD ? 1 : 0, input, cullFunctor, tagBits, tagMask); - task.addBranch("RenderForwardTask", 1, input); + task.addBranch("RenderForwardTask", RENDER_FORWARD ? 0 : 1, input); } void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { @@ -45,11 +48,11 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: // Assemble the lighting stages current frames const auto lightingStageFramesAndZones = task.addJob("AssembleStages", items); - if (!DISABLE_DEFERRED) { +#ifndef Q_OS_ANDROID const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); - } else { +#else const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("RenderForwardTask", renderInput); - } +#endif } diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 8ef0dc0d73..d3ea20273e 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -67,11 +67,11 @@ float TextRenderer3D::getFontSize() const { } void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, - const glm::vec2& bounds, bool layered) { + const glm::vec2& bounds, bool forward) { // The font does all the OpenGL work if (_font) { _color = color; - _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, layered); + _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, forward); } } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 6c91411e1d..ce4dd9f9e5 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -38,8 +38,8 @@ public: glm::vec2 computeExtent(const QString& str) const; float getFontSize() const; // Pixel size - void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(1.0f), - const glm::vec2& bounds = glm::vec2(-1.0f), bool layered = false); + void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, + const glm::vec2& bounds, bool forward); private: TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false, diff --git a/libraries/render-utils/src/forward_grid.slf b/libraries/render-utils/src/forward_grid.slf new file mode 100644 index 0000000000..e34794bfed --- /dev/null +++ b/libraries/render-utils/src/forward_grid.slf @@ -0,0 +1,40 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gondelman on 5/9/19 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/ShaderConstants.h@> +<@include gpu/Paint.slh@> + +struct Grid { + vec4 period; + vec4 offset; + vec4 edge; +}; + +LAYOUT(binding=0) uniform gridBuffer { + Grid grid; +}; + +layout(location=GPU_ATTR_TEXCOORD0) in vec2 varTexCoord0; +layout(location=GPU_ATTR_COLOR) in vec4 varColor; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + float alpha = mix(paintGridMajorMinor(varTexCoord0, grid.offset, grid.period, grid.edge), + paintGrid(varTexCoord0, grid.offset.xy, grid.period.xy, grid.edge.xy), + float(grid.edge.z == 0.0)); + + if (alpha < 0.0001) { + discard; + } + + _fragColor0 = vec4(varColor.xyz, 1.0); +} diff --git a/libraries/render-utils/src/forward_grid_translucent.slf b/libraries/render-utils/src/forward_grid_translucent.slf new file mode 100644 index 0000000000..df0494a22e --- /dev/null +++ b/libraries/render-utils/src/forward_grid_translucent.slf @@ -0,0 +1,41 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gondelman on 5/9/19 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/ShaderConstants.h@> +<@include gpu/Paint.slh@> + +struct Grid { + vec4 period; + vec4 offset; + vec4 edge; +}; + +LAYOUT(binding=0) uniform gridBuffer { + Grid grid; +}; + +layout(location=GPU_ATTR_TEXCOORD0) in vec2 varTexCoord0; +layout(location=GPU_ATTR_COLOR) in vec4 varColor; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + float alpha = mix(paintGridMajorMinor(varTexCoord0, grid.offset, grid.period, grid.edge), + paintGrid(varTexCoord0, grid.offset.xy, grid.period.xy, grid.edge.xy), + float(grid.edge.z == 0.0)); + alpha *= varColor.w; + + if (alpha < 0.0001) { + discard; + } + + _fragColor0 = vec4(varColor.xyz, alpha); +} diff --git a/libraries/render-utils/src/forward_parabola.slf b/libraries/render-utils/src/forward_parabola.slf new file mode 100644 index 0000000000..b0def6db6b --- /dev/null +++ b/libraries/render-utils/src/forward_parabola.slf @@ -0,0 +1,18 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gondelman on 5/9/19 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +layout(location=0) in vec4 _color; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + _fragColor0 = _color; +} diff --git a/libraries/render-utils/src/parabola_forward.slv b/libraries/render-utils/src/parabola_forward.slv deleted file mode 100644 index 4eb1456666..0000000000 --- a/libraries/render-utils/src/parabola_forward.slv +++ /dev/null @@ -1,7 +0,0 @@ -layout(location=0) in vec4 _color; - -layout(location=0) out vec4 _fragColor0; - -void main(void) { - _fragColor0 = _color; -} \ No newline at end of file diff --git a/libraries/render-utils/src/render-utils/forward_grid.slp b/libraries/render-utils/src/render-utils/forward_grid.slp new file mode 100644 index 0000000000..c81b208f63 --- /dev/null +++ b/libraries/render-utils/src/render-utils/forward_grid.slp @@ -0,0 +1 @@ +VERTEX standardTransformPNTC diff --git a/libraries/render-utils/src/render-utils/forward_grid_translucent.slp b/libraries/render-utils/src/render-utils/forward_grid_translucent.slp new file mode 100644 index 0000000000..c81b208f63 --- /dev/null +++ b/libraries/render-utils/src/render-utils/forward_grid_translucent.slp @@ -0,0 +1 @@ +VERTEX standardTransformPNTC diff --git a/libraries/render-utils/src/render-utils/forward_parabola.slp b/libraries/render-utils/src/render-utils/forward_parabola.slp new file mode 100644 index 0000000000..ab3f1d4126 --- /dev/null +++ b/libraries/render-utils/src/render-utils/forward_parabola.slp @@ -0,0 +1 @@ +VERTEX parabola diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 364e24c5ac..c69db5e055 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -13,10 +13,13 @@ #include "FontFamilies.h" #include "../StencilMaskPass.h" -#include "DisableDeferred.h" - static std::mutex fontMutex; +gpu::PipelinePointer Font::_deferredPipeline; +gpu::PipelinePointer Font::_forwardPipeline; +gpu::PipelinePointer Font::_transparentPipeline; +gpu::Stream::FormatPointer Font::_format; + struct TextureVertex { glm::vec2 pos; glm::vec2 tex; @@ -218,13 +221,10 @@ void Font::read(QIODevice& in) { } void Font::setupGPU() { - if (!_initialized) { - _initialized = true; - + if (!_deferredPipeline) { // Setup render pipeline { { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::forward_sdf_text3D); auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); @@ -232,25 +232,11 @@ void Font::setupGPU() { gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMaskDrawShape(*state); - _layeredPipeline = gpu::Pipeline::create(program, state); - } - - if (DISABLE_DEFERRED) { - _pipeline = _layeredPipeline; - } else { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D); - auto state = std::make_shared(); - state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMaskDrawShape(*state); - _pipeline = gpu::Pipeline::create(program, state); + _deferredPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D), state); + _forwardPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::forward_sdf_text3D), state); } { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent); auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); @@ -258,7 +244,7 @@ void Font::setupGPU() { gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMask(*state); - _transparentPipeline = gpu::Pipeline::create(program, state); + _transparentPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent), state); } } @@ -363,7 +349,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm } void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color, - EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool layered) { + EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool forward) { if (str == "") { return; } @@ -390,7 +376,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString } // need the gamma corrected color here - batch.setPipeline(color.a < 1.0f ? _transparentPipeline : (layered ? _layeredPipeline : _pipeline)); + batch.setPipeline(color.a < 1.0f ? _transparentPipeline : (forward ? _forwardPipeline : _deferredPipeline)); batch.setInputFormat(_format); batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 28af5bac43..893ab59981 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -46,7 +46,7 @@ public: // Render string to batch void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, EffectType effectType, - const glm::vec2& origin, const glm::vec2& bound, bool layered); + const glm::vec2& origin, const glm::vec2& bound, bool forward); static Pointer load(const QString& family); @@ -77,15 +77,13 @@ private: float _descent = 0.0f; float _spaceWidth = 0.0f; - bool _initialized = false; - - // gpu structures - gpu::PipelinePointer _pipeline; - gpu::PipelinePointer _layeredPipeline; - gpu::PipelinePointer _transparentPipeline; gpu::TexturePointer _texture; - gpu::Stream::FormatPointer _format; gpu::BufferStreamPointer _stream; + + static gpu::PipelinePointer _deferredPipeline; + static gpu::PipelinePointer _forwardPipeline; + static gpu::PipelinePointer _transparentPipeline; + static gpu::Stream::FormatPointer _format; }; #endif diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 953a0b8223..7821692a60 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -64,6 +64,7 @@ namespace render { public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; + enum RenderMethod { DEFERRED, FORWARD }; enum DebugFlags { RENDER_DEBUG_NONE = 0, RENDER_DEBUG_HULLS = 1 @@ -77,6 +78,7 @@ namespace render { float lodAngleHalfTan = 0.1f, RenderMode renderMode = DEFAULT_RENDER_MODE, DisplayMode displayMode = MONO, + RenderMethod renderMethod = DEFERRED, DebugFlags debugFlags = RENDER_DEBUG_NONE, gpu::Batch* batch = nullptr) : _context(context), @@ -86,6 +88,7 @@ namespace render { _lodAngleHalfTanSq(lodAngleHalfTan * lodAngleHalfTan), _renderMode(renderMode), _displayMode(displayMode), + _renderMethod(renderMethod), _debugFlags(debugFlags), _batch(batch) { } @@ -117,6 +120,7 @@ namespace render { RenderMode _renderMode { DEFAULT_RENDER_MODE }; DisplayMode _displayMode { MONO }; + RenderMethod _renderMethod { DEFERRED }; DebugFlags _debugFlags { RENDER_DEBUG_NONE }; gpu::Batch* _batch = nullptr; diff --git a/libraries/shared/src/DisableDeferred.h b/libraries/shared/src/DisableDeferred.h deleted file mode 100644 index 9a1f9be117..0000000000 --- a/libraries/shared/src/DisableDeferred.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Sam Gondelman on 3/7/19. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_DisableDeferred_h -#define hifi_DisableDeferred_h - -#include -#include - -#if defined(USE_GLES) -static bool DISABLE_DEFERRED = true; -#else -static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" }; -static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); -#endif - - -#endif // hifi_DisableDeferred_h - diff --git a/libraries/shared/src/RenderForward.h b/libraries/shared/src/RenderForward.h new file mode 100644 index 0000000000..e6fdef255b --- /dev/null +++ b/libraries/shared/src/RenderForward.h @@ -0,0 +1,24 @@ +// +// Created by Sam Gondelman on 3/7/19. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderForward_h +#define hifi_RenderForward_h + +#include +#include + +#if defined(USE_GLES) +// This isn't necessary since android forces the forward renderer, but just in case +static bool RENDER_FORWARD = true; +#else +static const QString RENDER_FORWARD_STRING { "HIFI_RENDER_FORWARD" }; +static bool RENDER_FORWARD = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD_STRING); +#endif + + +#endif // hifi_RenderForward_h \ No newline at end of file diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 87fb21d59b..cdc279ff36 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -460,7 +460,7 @@ public: void applyConfiguration() override { TimeProfiler probe("configure::" + JobConcept::getName()); jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); - for (auto& branch : _branches) { + for (auto& branch : SwitchConcept::_branches) { branch.second.applyConfiguration(); } } @@ -468,13 +468,9 @@ public: void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); if (config->isEnabled()) { - auto jobsIt = _branches.find(config->getBranch()); - if (jobsIt != _branches.end()) { + auto jobsIt = SwitchConcept::_branches.find(config->getBranch()); + if (jobsIt != SwitchConcept::_branches.end()) { jobsIt->second.run(jobContext); - if (jobContext->taskFlow.doAbortTask()) { - jobContext->taskFlow.reset(); - return; - } } } } From 55138ed0886bd7c4c3bd50fa02896fdf75f37ac3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 13 May 2019 14:11:34 -0700 Subject: [PATCH 74/83] CR feedback --- .../resources/describe-settings.json | 1378 ++++++++--------- libraries/entities/src/EntityTree.cpp | 26 +- 2 files changed, 701 insertions(+), 703 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index d3a550b801..488b2091f3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -221,138 +221,138 @@ ] }, { - "name": "standard_permissions", - "type": "table", - "label": "Domain-Wide User Permissions", - "help": "Indicate which types of users can have which domain-wide permissions.", - "caption": "Standard Permissions", - "can_add_new_rows": false, - "groups": [ - { - "label": "Type of User", - "span": 1 - }, - { - "label": "Permissions ?", - "span": 11 - } - ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ], - "non-deletable-row-key": "permissions_id", - "non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ], - "default": [ - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "anonymous" - }, - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "friends" - }, - { - "id_can_adjust_locks": true, - "id_can_connect": true, - "id_can_connect_past_max_capacity": true, - "id_can_kick": true, - "id_can_replace_content": true, - "id_can_rez": true, - "id_can_rez_certified": true, - "id_can_rez_tmp": true, - "id_can_rez_tmp_certified": true, - "id_can_write_to_asset_server": true, - "id_can_get_and_set_private_user_data": true, - "permissions_id": "localhost" - }, - { - "id_can_connect": true, - "id_can_rez_tmp_certified": true, - "permissions_id": "logged-in" - } - ] + "name": "standard_permissions", + "type": "table", + "label": "Domain-Wide User Permissions", + "help": "Indicate which types of users can have which domain-wide permissions.", + "caption": "Standard Permissions", + "can_add_new_rows": false, + "groups": [ + { + "label": "Type of User", + "span": 1 + }, + { + "label": "Permissions ?", + "span": 11 + } + ], + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ], + "non-deletable-row-key": "permissions_id", + "non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ], + "default": [ + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "anonymous" + }, + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "friends" + }, + { + "id_can_adjust_locks": true, + "id_can_connect": true, + "id_can_connect_past_max_capacity": true, + "id_can_kick": true, + "id_can_replace_content": true, + "id_can_rez": true, + "id_can_rez_certified": true, + "id_can_rez_tmp": true, + "id_can_rez_tmp_certified": true, + "id_can_write_to_asset_server": true, + "id_can_get_and_set_private_user_data": true, + "permissions_id": "localhost" + }, + { + "id_can_connect": true, + "id_can_rez_tmp_certified": true, + "permissions_id": "logged-in" + } + ] }, { "name": "group_permissions", @@ -369,118 +369,118 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "Group Name", - "readonly": true, - "hidden": true - }, - { - "name": "rank_id", - "label": "Rank ID", - "readonly": true, - "hidden": true - }, - { - "name": "rank_order", - "label": "Rank Order", - "readonly": true, - "hidden": true - }, - { - "name": "rank_name", - "label": "", - "readonly": true - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "Group Name", + "readonly": true, + "hidden": true + }, + { + "name": "rank_id", + "label": "Rank ID", + "readonly": true, + "hidden": true + }, + { + "name": "rank_order", + "label": "Rank Order", + "readonly": true, + "hidden": true + }, + { + "name": "rank_name", + "label": "", + "readonly": true + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "group_forbiddens", @@ -497,115 +497,115 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "Group Name", - "hidden": true - }, - { - "name": "rank_id", - "label": "Rank ID", - "hidden": true - }, - { - "name": "rank_order", - "label": "Rank Order", - "hidden": true - }, - { - "name": "rank_name", - "label": "", - "readonly": true - }, - { - "name": "group_id", - "label": "Group ID", - "readonly": true, - "hidden": true - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "Group Name", + "hidden": true + }, + { + "name": "rank_id", + "label": "Rank ID", + "hidden": true + }, + { + "name": "rank_order", + "label": "Rank Order", + "hidden": true + }, + { + "name": "rank_name", + "label": "", + "readonly": true + }, + { + "name": "group_id", + "label": "Group ID", + "readonly": true, + "hidden": true + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "permissions", @@ -618,93 +618,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "ip_permissions", @@ -717,93 +717,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "mac_permissions", @@ -816,93 +816,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "machine_fingerprint_permissions", @@ -915,93 +915,93 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 11 + "label": "Permissions ?", + "span": 11 } ], - "columns": [ - { - "name": "permissions_id", - "label": "" - }, - { - "name": "id_can_connect", - "label": "Connect", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_adjust_locks", - "label": "Lock / Unlock", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez", - "label": "Rez", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp", - "label": "Rez Temporary", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_certified", - "label": "Rez Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_rez_tmp_certified", - "label": "Rez Temporary Certified", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_write_to_asset_server", - "label": "Write Assets", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_connect_past_max_capacity", - "label": "Ignore Max Capacity", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_kick", - "label": "Kick Users", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_replace_content", - "label": "Replace Content", - "type": "checkbox", - "editable": true, - "default": false - }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } - ] + "columns": [ + { + "name": "permissions_id", + "label": "" + }, + { + "name": "id_can_connect", + "label": "Connect", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_adjust_locks", + "label": "Lock / Unlock", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez", + "label": "Rez", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp", + "label": "Rez Temporary", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_write_to_asset_server", + "label": "Write Assets", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_connect_past_max_capacity", + "label": "Ignore Max Capacity", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_kick", + "label": "Kick Users", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_replace_content", + "label": "Replace Content", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } + ] }, { "name": "multi_kick_logged_in", diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index cc4f526646..f46d7c3a0a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1871,21 +1871,19 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } - if (!properties.getPrivateUserData().isEmpty() && validEditPacket) { - if (!senderNode->getCanGetAndSetPrivateUserData()) { - if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() - << "] is attempting to set private user data but user isn't allowed; edit rejected..."; - } + if (!properties.getPrivateUserData().isEmpty() && validEditPacket && !senderNode->getCanGetAndSetPrivateUserData()) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] is attempting to set private user data but user isn't allowed; edit rejected..."; + } - // If this was an add, we also want to tell the client that sent this edit that the entity was not added. - if (isAdd) { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - validEditPacket = false; - } else { - suppressDisallowedPrivateUserData = true; - } + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + } else { + suppressDisallowedPrivateUserData = true; } } From 652ac17dacffc1c31180803f61d85721651a52b7 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 13 May 2019 14:13:48 -0700 Subject: [PATCH 75/83] fix deadlock on startup --- libraries/shared/src/DependencyManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 8eddb572cf..00faba0e89 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -89,7 +89,7 @@ private: QHash> _instanceHash; QHash _inheritanceHash; - mutable QMutex _instanceHashMutex; + mutable QMutex _instanceHashMutex { QMutex::Recursive }; mutable QMutex _inheritanceHashMutex; bool _exiting { false }; From ede8859cc777ef1ea5473c0c083777b754519b00 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 13 May 2019 14:38:34 -0700 Subject: [PATCH 76/83] Call correct NodeList::reset() from Application --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 678f737f25..bec12388ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1298,8 +1298,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); // you might think we could just do this in NodeList but we only want this connection for Interface - connect(&nodeList->getDomainHandler(), SIGNAL(limitOfSilentDomainCheckInsReached()), - nodeList.data(), SLOT(reset())); + connect(&nodeList->getDomainHandler(), &DomainHandler::limitOfSilentDomainCheckInsReached, + [nodeList]() {nodeList->reset("Domain checkin limit"); }); auto dialogsManager = DependencyManager::get(); #if defined(Q_OS_ANDROID) From 2e2d0add93ce18fbbbc5eba2c9dcc4751010bb5d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 May 2019 13:04:46 -0700 Subject: [PATCH 77/83] BUGZ-89: Suppress getTransform spam --- libraries/shared/src/SpatiallyNestable.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 866698adeb..f49921f272 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -758,7 +758,11 @@ const Transform SpatiallyNestable::getTransform() const { bool success; Transform result = getTransform(success); if (!success) { - qCDebug(shared) << "getTransform failed for" << getID(); + // There is a known issue related to child entities not being deleted + // when their parent is removed. This has the side-effect that the + // logs will be spammed with the following message. Until this is + // fixed, this log message will be suppressed. + //qCDebug(shared) << "getTransform failed for" << getID(); } return result; } From 70ec84177aa808ae4a8d906f51a8ff9965ea19b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 14 May 2019 10:32:28 +1200 Subject: [PATCH 78/83] Doc review --- interface/src/raypick/CollisionPick.cpp | 2 +- interface/src/raypick/PickScriptingInterface.cpp | 2 +- libraries/shared/src/PhysicsCollisionGroups.h | 2 +- libraries/shared/src/RegisteredMetaTypes.h | 9 +++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index b3888ae3a0..83ed61030e 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -61,7 +61,7 @@ void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::v * An intersection result for a collision pick. * * @typedef {object} CollisionPickResult - * @property {boolean} intersects - truen if there is at least one intersection, false if there isn't. + * @property {boolean} intersects - true if there is at least one intersection, false if there isn't. * @property {IntersectingObject[]} intersectingObjects - All objects which intersect with the collisionRegion. * @property {CollisionRegion} collisionRegion - The collision region that was used. Valid even if there was no intersection. */ diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index dff68f3ded..82d00d803f 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -325,7 +325,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti * @property {number} threshold - The approximate minimum penetration depth for a test object to be considered in contact with * the collision region. The depth is in world coordinates but scales with the parent if defined. * @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision - * masks overlap with the regions's collision group are considered to be colliding with the region. + * masks overlap with the region's collision group are considered to be colliding with the region. */ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index 7323133876..08fc5f5ac0 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -72,7 +72,7 @@ const int32_t BULLET_COLLISION_MASK_DETAILED_RAY = BULLET_COLLISION_GROUP_DETAIL const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; /**jsdoc - *

A collision may be with the following types of items:

+ *

A collision may occur with the following types of items:

*
Flag nameValueDescription
Flag NameValueDescription
PICK_DOMAIN_ENTITIES1Include domain entities when intersecting.
* * diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 25a1792fe2..dfd388230f 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -426,15 +426,16 @@ public: /**jsdoc * A volume for checking collisions in the physics simulation. * @typedef {object} CollisionRegion - * @property {Shape} shape - The collision region's shape and size. Dimensions are in world space, but scale with the parent - * if defined. - * @property {boolean} loaded - true if the shape has no model, or has a model and it is loaded. + * @property {Shape} shape - The collision region's shape and size. Dimensions are in world coordinates, but scale with the + * parent if defined. + * @property {boolean} loaded - true if the shape has no model, or has a model and it is loaded, + * false if otherwise. * @property {Vec3} position - The position of the collision region, relative to the parent if defined. * @property {Quat} orientation - The orientation of the collision region, relative to the parent if defined. * @property {number} threshold - The approximate minimum penetration depth for a test object to be considered in contact with * the collision region. The depth is in world coordinates but scales with the parent if defined. * @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision - * masks overlap with the regions's collision group are considered to be colliding with the region. + * masks overlap with the region's collision group are considered to be colliding with the region. */ /**jsdoc From 305a215d21d000d023e24b6d03a9673b0c5ffb2d Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 13 May 2019 15:50:04 -0700 Subject: [PATCH 79/83] Pass a QObject context for the functor --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bec12388ca..34efe1ba71 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1299,7 +1299,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // you might think we could just do this in NodeList but we only want this connection for Interface connect(&nodeList->getDomainHandler(), &DomainHandler::limitOfSilentDomainCheckInsReached, - [nodeList]() {nodeList->reset("Domain checkin limit"); }); + nodeList.data(), [nodeList]() {nodeList->reset("Domain checkin limit"); }); auto dialogsManager = DependencyManager::get(); #if defined(Q_OS_ANDROID) From 2ce9e9ee10e438bc5ddcdf883e9e3f65c3875fe2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 13 May 2019 16:07:29 -0700 Subject: [PATCH 80/83] The right way to restrict private user data: --- .../src/entities/EntityTreeSendThread.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 12 ++++++++---- libraries/entities/src/EntityItem.h | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index a6542689e0..2b964ae674 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -372,7 +372,7 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream // Record explicitly filtered-in entity so that extra entities can be flagged. entityNodeData->insertSentFilteredEntity(entityID); } - OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData); + OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData, entityNode->getCanGetAndSetPrivateUserData()); if (appendEntityState != OctreeElement::COMPLETED) { if (appendEntityState == OctreeElement::PARTIAL) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6054a4cd43..0d509d0e27 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -155,7 +155,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param } OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const { + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + const bool destinationNodeCanGetAndSetPrivateUserData) const { // ALL this fits... // object ID [16 bytes] @@ -199,6 +200,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); } + QString privateUserData = ""; + if (destinationNodeCanGetAndSetPrivateUserData) { + privateUserData = getPrivateUserData(); + } + EntityPropertyFlags propertiesDidntFit = requestedProperties; LevelDetails entityLevel = packetData->startLevel(); @@ -277,9 +283,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); - if (nodeList->getThisNodeCanGetAndSetPrivateUserData()) { - APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, getPrivateUserData()); - } + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, privateUserData); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 6e86e040d5..6763ca82b1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -134,7 +134,8 @@ public: virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const; + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + const bool destinationNodeCanGetAndSetPrivateUserData = false) const; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, From aeff5d93d88803b4d6f01b437359787fb8219483 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 13 May 2019 16:57:38 -0700 Subject: [PATCH 81/83] try a longer timeout before we removeSilentNodes --- libraries/networking/src/LimitedNodeList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 8593ad4b1b..4db4f3136a 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -49,7 +49,7 @@ const int INVALID_PORT = -1; -const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000; +const quint64 NODE_SILENCE_THRESHOLD_MSECS = 20 * 1000; static const size_t DEFAULT_MAX_CONNECTION_RATE { std::numeric_limits::max() }; From 8b2d12e2f743b50e899ab69796e24de11834aba0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 9 May 2019 09:00:27 -0700 Subject: [PATCH 82/83] when an avatar entity is deleted, also delete its descendants --- interface/src/avatar/AvatarManager.cpp | 25 ++++++++++++++++++++++--- libraries/entities/src/EntityItem.cpp | 4 ++++ libraries/entities/src/EntityItem.h | 1 + 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index cea1a4a654..df620b9a08 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -532,13 +532,32 @@ void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction } void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities) { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; for (auto entity : deadEntities) { - QUuid sessionID = entity->getOwningAvatarID(); - AvatarSharedPointer avatar = getAvatarBySessionID(sessionID); + QUuid entityOwnerID = entity->getOwningAvatarID(); + AvatarSharedPointer avatar = getAvatarBySessionID(entityOwnerID); + const bool REQUIRES_REMOVAL_FROM_TREE = false; if (avatar) { - const bool REQUIRES_REMOVAL_FROM_TREE = false; avatar->clearAvatarEntity(entity->getID(), REQUIRES_REMOVAL_FROM_TREE); } + if (entityTree && entity->isMyAvatarEntity()) { + entityTree->withWriteLock([&] { + // We only need to delete the direct children (rather than the descendants) because + // when the child is deleted, it will take care of its own children. If the child + // is also an avatar-entity, we'll end up back here. If it's not, the entity-server + // will take care of it in the usual way. + entity->forEachChild([&](SpatiallyNestablePointer child) { + EntityItemPointer childEntity = std::dynamic_pointer_cast(child); + if (childEntity) { + entityTree->deleteEntity(childEntity->getID(), true, true); + if (avatar) { + avatar->clearAvatarEntity(childEntity->getID(), REQUIRES_REMOVAL_FROM_TREE); + } + } + }); + }); + } } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d91c11e726..4232e9d1b9 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3431,6 +3431,10 @@ bool EntityItem::isWearable() const { (getParentID() == DependencyManager::get()->getSessionUUID() || getParentID() == AVATAR_SELF_ID); } +bool EntityItem::isMyAvatarEntity() const { + return _hostType == entity::HostType::AVATAR && Physics::getSessionUUID() == _owningAvatarID; +}; + void EntityItem::addGrab(GrabPointer grab) { enableNoBootstrap(); SpatiallyNestable::addGrab(grab); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ea4b11e0b0..a8c3d31344 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -503,6 +503,7 @@ public: virtual bool isWearable() const; bool isDomainEntity() const { return _hostType == entity::HostType::DOMAIN; } bool isAvatarEntity() const { return _hostType == entity::HostType::AVATAR; } + bool isMyAvatarEntity() const; bool isLocalEntity() const { return _hostType == entity::HostType::LOCAL; } entity::HostType getEntityHostType() const { return _hostType; } virtual void setEntityHostType(entity::HostType hostType) { _hostType = hostType; } From 7d8e14f51055dfbf00a0d42da57077c958a14e40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 13 May 2019 18:19:29 -0700 Subject: [PATCH 83/83] Additional silencing of watchdog spam --- interface/src/Application.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0e8c03e032..a8884d508e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -441,6 +441,7 @@ public: auto elapsedMovingAverage = _movingAverage.getAverage(); if (elapsedMovingAverage > _maxElapsedAverage) { +#if !defined(NDEBUG) qCDebug(interfaceapp_deadlock) << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage @@ -448,9 +449,11 @@ public: << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage << "NEW maxElapsedAverage:" << elapsedMovingAverage << "** NEW MAX ELAPSED AVERAGE **" << "samples:" << _movingAverage.getSamples(); +#endif _maxElapsedAverage = elapsedMovingAverage; } if (lastHeartbeatAge > _maxElapsed) { +#if !defined(NDEBUG) qCDebug(interfaceapp_deadlock) << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage @@ -458,8 +461,11 @@ public: << "NEW maxElapsed:" << lastHeartbeatAge << "** NEW MAX ELAPSED **" << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); +#endif _maxElapsed = lastHeartbeatAge; } + +#if !defined(NDEBUG) if (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT) { qCDebug(interfaceapp_deadlock) << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge @@ -468,6 +474,7 @@ public: << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); } +#endif if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { qCDebug(interfaceapp_deadlock) << "DEADLOCK DETECTED -- "
ValueDescription