From 231d1373b6ac8c48c09f1e9d8e408fcb85fb9878 Mon Sep 17 00:00:00 2001
From: David Rowe Locomotion control types.true
if the avatar is in a "hero" zone, false
if it isn't.
+ * Read-only.
*
* @example
+ *
+ *
+ * @typedef {number} MyAvatar.LocomotionControlsMode
+ */
enum LocomotionControlsMode {
CONTROLS_DEFAULT = 0,
CONTROLS_ANALOG,
@@ -128,6 +145,8 @@ class MyAvatar : public Avatar {
* avatar. Read-only.
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. Read-only.
+ * @property {boolean} hasPriority -
+ *
+ *
+ * Value Name Description
+ * 0
Default Your walking speed is constant; it doesn't change depending on how far
+ * forward you push your controller's joystick. Fully pushing your joystick forward makes your avatar run.
+ * 1
Analog Your walking speed changes in steps based on how far forward you push your
+ * controller's joystick. Fully pushing your joystick forward makes your avatar run.
+ *
+ * 2
AnalogPlus Your walking speed changes proportionally to how far forward you push
+ * your controller's joystick. Fully pushing your joystick forward makes your avatar run. true
if the avatar is in a "hero" zone, false
if it isn't.
+ * Read-only.
*
* @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here.
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
@@ -239,9 +258,14 @@ class MyAvatar : public Avatar {
* where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated.
* Read-only.
*
- * @property {number} walkSpeed - The walk speed of your avatar.
- * @property {number} walkBackwardSpeed - The walk backward speed of your avatar.
- * @property {number} sprintSpeed - The sprint speed of your avatar.
+ * @property {number} walkSpeed - The walk speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} walkBackwardSpeed - The walk backward speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} sprintSpeed - The sprint (run) speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} analogPlusWalkSpeed - The walk speed of your avatar for the "AnalogPlus" control scheme.
+ * @property {number} analogPlusSprintSpeed - The sprint speed of your avatar for the "AnalogPlus" control scheme.
* @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior.
* @property {number} isInSittingState - true
if your avatar is sitting (avatar leaning is disabled,
* recenntering is enabled), false
if it is standing (avatar leaning is enabled, and avatar recenters if it
@@ -281,6 +305,7 @@ class MyAvatar : public Avatar {
* @borrows Avatar.updateAvatarEntity as updateAvatarEntity
* @borrows Avatar.clearAvatarEntity as clearAvatarEntity
* @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected
+ * @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL
* @borrows Avatar.getAttachmentData as getAttachmentData
* @borrows Avatar.setAttachmentData as setAttachmentData
* @borrows Avatar.attach as attach
@@ -308,7 +333,6 @@ class MyAvatar : public Avatar {
* @comment Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame - Don't borrow because implementation is different.
* @borrows Avatar.getTargetScale as getTargetScale
* @borrows Avatar.resetLastSent as resetLastSent
- * @borrows Avatar.hasPriority as hasPriority
*/
// FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type
Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition)
@@ -789,26 +813,34 @@ public:
*/
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
- /**
+ /**jsdoc
+ * Gets the control scheme that is in use.
* @function MyAvatar.getControlScheme
- * @returns {number}
- */
+ * @returns {MyAvatar.LocomotionControlsMode} The control scheme that is in use.
+ */
Q_INVOKABLE int getControlScheme() const { return _controlSchemeIndex; }
- /**
+ /**jsdoc
+ * Sets the control scheme to use.
* @function MyAvatar.setControlScheme
- * @param {number} index
- */
+ * @param {MyAvatar.LocomotionControlsMode} controlScheme - The control scheme to use.
+ */
Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; }
/**jsdoc
+ * Gets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.hoverWhenUnsupported
- * @returns {boolean}
+ * @returns {boolean} true
if your avatar hovers when its feet are not on the ground, false
if it
+ * falls.
*/
+ // FIXME: Should be named, getHoverWhenUnsupported().
Q_INVOKABLE bool hoverWhenUnsupported() const { return _hoverWhenUnsupported; }
+
/**jsdoc
+ * Sets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.setHoverWhenUnsupported
- * @param {boolean} on
+ * @param {boolean} hover - true
if your avatar hovers when its feet are not on the ground, false
+ * if it falls.
*/
Q_INVOKABLE void setHoverWhenUnsupported(bool on) { _hoverWhenUnsupported = on; }
@@ -826,15 +858,19 @@ public:
* @returns {string} "left"
for the left hand, "right"
for the right hand.
*/
Q_INVOKABLE QString getDominantHand() const;
+
/**jsdoc
- * @function MyAVatar.setStrafeEnabled
- * @param {bool} enabled
- */
+ * Sets whether strafing is enabled.
+ * @function MyAvatar.setStrafeEnabled
+ * @param {boolean} enabled - true
if strafing is enabled, false
if it isn't.
+ */
Q_INVOKABLE void setStrafeEnabled(bool enabled);
+
/**jsdoc
- * @function MyAvatar.getStrafeEnabled
- * @returns {bool}
- */
+ * Gets whether strafing is enabled.
+ * @function MyAvatar.getStrafeEnabled
+ * @returns {boolean} true
if strafing is enabled, false
if it isn't.
+ */
Q_INVOKABLE bool getStrafeEnabled() const;
/**jsdoc
* @function MyAvatar.setHmdAvatarAlignmentType
@@ -1495,18 +1531,8 @@ public:
*/
Q_INVOKABLE float getDriveGear5();
- /**jsdoc
- * Choose the control scheme.
- * @function MyAvatar.setControlSchemeIndex
- * @param {number} Choose the control scheme to be used.
- */
void setControlSchemeIndex(int index);
- /**jsdoc
- * Check what control scheme is in use.
- * @function MyAvatar.getControlSchemeIndex
- * @returns {number} Returns the index associated with a given control scheme.
- */
int getControlSchemeIndex();
/**jsdoc
@@ -2409,6 +2435,9 @@ private:
void updateEyeContactTarget(float deltaTime);
// These are made private for MyAvatar so that you will use the "use" methods instead
+ /**jsdoc
+ * @comment Borrows the base class's JSDoc.
+ */
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void updatePalms() override {}
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index e5131ff94b..5c62e0b2c6 100755
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -1211,6 +1211,12 @@ public:
const QString& getDisplayName() const { return _displayName; }
const QString& getSessionDisplayName() const { return _sessionDisplayName; }
bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; }
+
+ /**jsdoc
+ * Sets the avatar's skeleton model.
+ * @function Avatar.setSkeletonModelURL
+ * @param {string} url - The avatar's FST file.
+ */
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setDisplayName(const QString& displayName);
From f91c6dec3a24bfefc861ec9ef0c278e4c7973938 Mon Sep 17 00:00:00 2001
From: David Rowe
true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame to start the animation at.
+ * @param {number} lastFrame - The frame to end the animation at.
* @example overrideHandAnimation()
Gets the overrides the default hand poses that are triggered with controller buttons.
- * use {@link MyAvatar.restoreHandAnimation}. to restore the default poses.
+ * Overrides the default hand poses that are triggered with controller buttons.
+ * Use {@link MyAvatar.restoreHandAnimation} to restore the default poses.
* @function MyAvatar.overrideHandAnimation
- * @param isLeft {boolean} Set true if using the left hand
- * @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
+ * @param isLeft {boolean} true
to override the left hand, false
to override the right hand.
+ * @param {string} url - The URL of the animation file. Animation files need to be FBX format, but only need to contain the
* avatar skeleton and animation data.
- * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
- * @param loop {boolean} Set to true if the animation should loop.
- * @param firstFrame {number} The frame the animation should start at.
- * @param lastFrame {number} The frame the animation should end at
- * @example true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame to start the animation at.
+ * @param {number} lastFrame - The frame to end the animation at.
+ * @example The avatar animation system includes a set of default animations along with rules for how those animations are blended
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
* override the default animations. restoreHandAnimation()
is used to restore the default hand poses
- * If you aren't currently playing an override hand
- * animation, this function has no effect.
true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame the animation should start at.
+ * @param {number} lastFrame - The frame the animation should end at.
* @example true
to do snap turns in HMD mode; false
to do smooth turns in HMD mode.
*/
@@ -872,16 +871,17 @@ public:
* @returns {boolean} true
if strafing is enabled, false
if it isn't.
*/
Q_INVOKABLE bool getStrafeEnabled() const;
+
/**jsdoc
+ * Sets the HMD alignment relative to your avatar.
* @function MyAvatar.setHmdAvatarAlignmentType
* @param {string} type - "head"
to align your head and your avatar's head, "eyes"
to align your
* eyes and your avatar's eyes.
- *
*/
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& type);
/**jsdoc
- * Gets the HMD alignment for your avatar.
+ * Gets the HMD alignment relative to your avatar.
* @function MyAvatar.getHmdAvatarAlignmentType
* @returns {string} "head"
if aligning your head and your avatar's head, "eyes"
if aligning your
* eyes and your avatar's eyes.
@@ -1610,8 +1610,8 @@ public:
Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated
/**jsdoc
- * @comment Different behavior to the Avatar version of this method.
* Gets the rotation of a joint relative to the avatar.
+ * @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Quat} The rotation of the joint relative to the avatar.
@@ -1623,8 +1623,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
/**jsdoc
- * @comment Different behavior to the Avatar version of this method.
* Gets the translation of a joint relative to the avatar.
+ * @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Vec3} The translation of the joint relative to the avatar.
From caf29dc45a83ec0bb6e262c37943934f3df3a178 Mon Sep 17 00:00:00 2001
From: David Rowe Warning: Setting this value also sets the value of analogPlusSprintSpeed
to twice
+ * the value.
true
if your avatar is sitting (avatar leaning is disabled,
@@ -664,7 +666,7 @@ public:
* Restores the default hand animation state machine that is driven by the state machine in the avatar-animation JSON.
* The avatar animation system includes a set of default animations along with rules for how those animations are blended
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
- * override the default animations. restoreHandAnimation()
is used to restore the default hand poses
+ * override the default animations. restoreHandAnimation()
is used to restore the default hand poses.
* If you aren't currently playing an override hand animation, this function has no effect.
uuid
.
+ * @property {number} stableOrder - The order in which the button was created: each button created gets a value incremented by
+ * one.
+ *
+ * @property {string} icon - The url of the default button icon displayed. (50 x 50 pixels. SVG, PNG, or other image format.)
+ * @property {string} hoverIcon - The url of the button icon displayed when the button is hovered and not active.
+ * @property {string} activeIcon - The url of the button icon displayed when the button is active.
+ * @property {string} activeHoverIcon - The url of the button icon displayed when the button is hovered and active.
+ * @property {string} text - The button caption.
+ * @property {string} hoverText - The button caption when the button is hovered and not active.
+ * @property {string} activeText - The button caption when the button is active.
+ * @property {string} activeHoverText - The button caption when the button is hovered and active.
+ * @comment {string} defaultCaptionColor="#ffffff" - Internal property.
+ * @property {string} captionColor="#ffffff" - The color of the button caption.
+
+ * @property {boolean} isActive=false - true
if the button is active, false
if it isn't.
+ * @property {boolean} isEntered - true
if the button is being hovered, false
if it isn't.
+ * @property {boolean} buttonEnabled=true - true
if the button is enabled, false
if it is disabled.
+ * @property {number} sortOrder=100 - Determines the order of the buttons: buttons with lower numbers appear before buttons
+ * with larger numbers.
+ *
+ * @property {boolean} inDebugMode - If true
and the tablet is being used, the button's isActive
+ * state toggles each time the button is clicked. Tablet only.
+ *
+ * @comment {object} tabletRoot - Internal tablet-only property.
+ * @property {object} flickable - Internal tablet-only property.
+ * @property {object} gridView - Internal tablet-only property.
+ * @property {number} buttonIndex - Internal tablet-only property.
+ *
+ * @comment {number} imageOffOut - Internal toolbar-only property.
+ * @comment {number} imageOffIn - Internal toolbar-only property.
+ * @comment {number} imageOnOut - Internal toolbar-only property.
+ * @comment {number} imageOnIn - Internal toolbar-only property.
+ */
TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
_uuid(QUuid::createUuid()),
_stableOrder(++s_stableOrder),
@@ -977,6 +1017,7 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
_properties[UUID_KEY] = _uuid;
_properties[OBJECT_NAME_KEY] = _uuid.toString();
_properties[STABLE_ORDER_KEY] = _stableOrder;
+ // Other properties are defined in TabletButton.qml and ToolbarButton.qml.
if (QThread::currentThread() != qApp->thread()) {
qCWarning(uiLogging) << "Creating tablet button proxy on wrong thread";
}
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index 0b8dc95fa4..4e639d221f 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -39,6 +39,10 @@ class QmlWindowClass;
class OffscreenQmlSurface;
/**jsdoc
+ * The Tablet
API provides the facilities to work with the system or other tablet. In toolbar mode (Developer >
+ * UI > Tablet Becomes Toolbar), the tablet's menu buttons are displayed in a toolbar and other tablet content is displayed
+ * in a dialog.
+ *
* @namespace Tablet
*
* @hifi-interface
@@ -46,6 +50,8 @@ class OffscreenQmlSurface;
* @hifi-avatar
*/
/**jsdoc
+ * The tabletInterface
API provides the facilities to work with the system or other tablet.
+ *
* @namespace tabletInterface
*
* @hifi-interface
@@ -53,12 +59,17 @@ class OffscreenQmlSurface;
* @hifi-avatar
*
* @deprecated This API is deprecated and will be removed. Use {@link Tablet} instead.
+ *
+ * @borrows Tablet.getTablet as getTablet
+ * @borrows Tablet.playSound as playSound
+ * @borrows Tablet.tabletNotification as tabletNotification
*/
class TabletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
/**jsdoc
+ * Standard tablet sounds.
* Value | Description |
---|---|
2 | Tablet open. |
3 | Tablet hands in. |
4 | Tablet hands out. |
5 | Last. |
Note: Only triggered if the script is running in the same script engine as the script that created + * the tablet. Be default, this means in scripts included as part of the default scripts.
* @function Tablet.tabletNotification * @returns {Signal} */ - /**jsdoc - * Triggered when a tablet message or dialog is created. - * @function tabletInterface.tabletNotification - * @returns {Signal} - */ void tabletNotification(); private: @@ -149,7 +154,9 @@ protected: }; /**jsdoc - * @typedef {object} TabletProxy#ButtonList + * Information on the buttons in the tablet main menu (toolbar in toolbar mode) for use in QML. Has properties and functions + * per http://doc.qt.io/qt-5/qabstractlistmodel.html. + * @typedef {object} TabletProxy.TabletButtonListModel */ class TabletButtonListModel : public QAbstractListModel { Q_OBJECT @@ -203,18 +210,27 @@ private: Q_DECLARE_METATYPE(TabletButtonsProxyModel*); /**jsdoc + * An instance of a tablet. In toolbar mode (Developer > + * UI > Tablet Becomes Toolbar), the tablet's menu buttons are displayed in a toolbar and other tablet content is displayed + * in a dialog. + * + *Create a new tablet or retrieve an existing tablet using {@link Tablet.getTablet}.
+ * * @class TabletProxy - * + * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {string} name - Name of this tablet. Read-only. - * @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode. - * When tablet is in toolbar mode, all its buttons will appear in a floating toolbar. - * @property {boolean} landscape - * @property {boolean} tabletShown Read-only. - * @property {TabletProxy#ButtonList} buttons Read-only. + * @property {string} name - A unique name that identifies the tablet. Read-only. + * @property {boolean} toolbarMode -true
if the tablet is in toolbar mode, false
if it isn't.
+ * @property {boolean} landscape - true
if the tablet is displayed in landscape mode, false
if it is
+ * displayed in portrait mode.
+ * Note: This property isn't used in toolbar mode.
+ * @property {boolean} tabletShown -true
if the tablet is currently displayed, false
if it isn't.
+ * Note: This property isn't used in toolbar mode.
+ * @property {TabletProxy.TabletButtonListModel} buttons - Information on the buttons in the tablet main menu (or toolbar in + * toolbar mode) for use in QML. Read-only. */ class TabletProxy : public QObject { Q_OBJECT @@ -235,140 +251,190 @@ public: void unfocus(); /**jsdoc + * Displays the tablet menu. The tablet is opened if it isn't already open. * @function TabletProxy#gotoMenuScreen - * @param {string} [submenu=""] + * @param {string} [submenu=""] - The name of a submenu to display, if any. + * @exampletrue
, the web page or app is displayed in a frame with "back"
+ * and "close" buttons.
*/
Q_INVOKABLE void gotoWebScreen(const QString& url);
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false);
/**jsdoc
+ * Opens a QML app or dialog on the tablet.
* @function TabletProxy#loadQMLSource
- * @param {string} path
- * @param {boolean} [resizable=false]
+ * @param {string} path - The path of the QML app or dialog.
+ * @param {boolean} [resizable=false] - true
to make the dialog resizable in toolbar mode, false
+ * to have it not resizable.
*/
Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false);
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
// it should be initialized internally so it cannot fail
/**jsdoc
+ * Displays a QML dialog over the top of the current dialog, without closing the current dialog. Use
+ * {@link TabletProxy#popFromStack|popFromStack} to close the dialog.
+ * If the current dialog or its ancestors contain a QML StackView
with objectName: "stack"
and
+ * function pushSource(path)
, that functions is called; otherwise,
+ * {@link TabletProxy#loadQMLSource|loadQMLSource} is called. The Create app provides an example of using a QML
+ * StackView
.
* @function TabletProxy#pushOntoStack
- * @param {string} path
- * @returns {boolean}
+ * @param {string} path - The path to the dialog's QML.
+ * @returns {boolean} true
if the dialog was successfully opened, false
if it wasn't.
*/
+ // edit.js provides an example of using this outside of main menu.
Q_INVOKABLE bool pushOntoStack(const QVariant& path);
/**jsdoc
+ * Closes a QML dialog that was displayed using {@link Tablet#pushOntoStack|pushOntoStack} with a dialog implementing a QML
+ * StackView
; otherwise, no action is taken.
+ * If using a QML StackView
, its popSource()
function is called.
* @function TabletProxy#popFromStack
*/
Q_INVOKABLE void popFromStack();
/**jsdoc
+ * Opens a QML app or dialog in addition to any current app. In tablet mode, the app or dialog is displayed over the top of
+ * the current app; in toolbar mode, the app or dialog is opened in a new window. If in tablet mode, the app can be closed
+ * using {@link TabletProxy#returnToPreviousApp}.
* @function TabletProxy#loadQMLOnTop
- * @param {string} path
+ * @param {string} path - The path to the app's QML.
*/
Q_INVOKABLE void loadQMLOnTop(const QVariant& path);
/**jsdoc
+ * Opens a web app or page in addition to any current app. In tablet mode, the app or page is displayed over the top of the
+ * current app; in toolbar mode, the app is opened in a new window. If in tablet mode, the app or page can be closed using
+ * {@link TabletProxy#returnToPreviousApp}.
* @function TabletProxy#loadWebScreenOnTop
- * @param {string} path
- * @param {string} [injectedJavaScriptURL=""]
+ * @param {string} path - The URL of the web page or HTML app.
+ * @param {string} [injectedJavaScriptURL=""] - The URL of JavaScript to inject into the web page.
*/
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url);
Q_INVOKABLE void loadWebScreenOnTop(const QVariant& url, const QString& injectedJavaScriptUrl);
/**jsdoc
+ * Closes the current app and returns to the previous app, if in tablet mode and the current app was loaded using
+ * {@link TabletProxy#loadQMLOnTop|loadQMLOnTop} or {@link TabletProxy#loadWebScreenOnTop|loadWebScreenOnTop}.
* @function TabletProxy#returnToPreviousApp
*/
Q_INVOKABLE void returnToPreviousApp();
/**jsdoc
- * Check if the tablet has a message dialog open.
+ * Checks if the tablet has a modal, non-modal, or message dialog open.
* @function TabletProxy#isMessageDialogOpen
- * @returns {boolean}
+ * @returns {boolean} true
if a modal, non-modal, or message dialog is open, false
if there isn't.
*/
Q_INVOKABLE bool isMessageDialogOpen();
/**jsdoc
- * Close any open dialogs.
- * @function TabletProxy#closeDialog
- */
+ * Closes any open modal, non-modal, or message dialog, opened by {@link Window.prompt}, {@link Window.promptAsync},
+ * {@link Window.openMessageBox}, or similar.
+ * @function TabletProxy#closeDialog
+ */
Q_INVOKABLE void closeDialog();
/**jsdoc
- * Creates a new button, adds it to this and returns it.
+ * Adds a new button to the tablet menu.
* @function TabletProxy#addButton
- * @param {object} properties - Button properties.
- * @returns {TabletButtonProxy}
+ * @param {TabletButtonProxy.ButtonProperties} properties - Button properties.
+ * @returns {TabletButtonProxy} The button added.
+ * @example Add a menu button.
+ * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ * var button = tablet.addButton({ text: "TEST" });
+ *
+ * button.clicked.connect(function () {
+ * print("TEST button clicked");
+ * });
+ *
+ * Script.scriptEnding.connect(function () {
+ * tablet.removeButton(button);
+ * });
*/
//FIXME: UI_TABLET_HACK: enumerate the button properties when we figure out what they should be!
Q_INVOKABLE TabletButtonProxy* addButton(const QVariant& properties);
/**jsdoc
- * Removes a button from the tablet.
+ * Removes a button from the tablet menu.
* @function TabletProxy#removeButton
- * @param {TabletButtonProxy} button - The button to be removed
+ * @param {TabletButtonProxy} button - The button to remove.
*/
Q_INVOKABLE void removeButton(TabletButtonProxy* tabletButtonProxy);
/**jsdoc
- * Used to send an event to the HTML/JavaScript embedded in the tablet.
+ * Sends a message to the current web page. To receive the message, the web page's script must connect to the
+ * EventBridge
that is automatically provided to the script:
+ * EventBridge.scriptEventReceived.connect(function(message) {
+ * ...
+ * });
* @function TabletProxy#emitScriptEvent
- * @param {object|string} message
+ * @param {string|object} message - The message to send to the web page.
*/
Q_INVOKABLE void emitScriptEvent(const QVariant& msg);
/**jsdoc
- * Used to send an event to the QML embedded in the tablet.
+ * Sends a message to the current QML page. To receive the message, the QML page must implement a function:
+ * function fromScript(message) {
+ * ...
+ * }
* @function TabletProxy#sendToQml
- * @param {object|string} message
+ * @param {string|object} message - The message to send to the QML page.
*/
Q_INVOKABLE void sendToQml(const QVariant& msg);
/**jsdoc
- * Check if the tablet is on the home screen.
+ * Checks if the tablet is on the home screen.
* @function TabletProxy#onHomeScreen
- * @returns {boolean}
+ * @returns {boolean} true
if the tablet is on the home screen, false
if it isn't.
*/
Q_INVOKABLE bool onHomeScreen();
/**jsdoc
- * Set tablet into or out of landscape mode.
+ * Sets whether the tablet is displayed in landscape or portrait mode.
+ * Note: The setting isn't used in toolbar mode.
* @function TabletProxy#setLandscape
- * @param {boolean} landscape - true
for landscape, false
for portrait.
+ * @param {boolean} landscape - true
to display the tablet in landscape mode, false
to display it
+ * in portrait mode.
*/
Q_INVOKABLE void setLandscape(bool landscape) { _landscape = landscape; }
/**jsdoc
+ * Gets whether the tablet is displayed in landscape or portrait mode.
+ * Note: The setting isn't used in toolbar mode.
* @function TabletProxy#getLandscape
- * @returns {boolean}
+ * @returns {boolean} true
if the tablet is displayed in landscape mode, false
if it is displayed
+ * in portrait mode.
*/
Q_INVOKABLE bool getLandscape() { return _landscape; }
/**jsdoc
+ * Checks if a path is the current app or dialog displayed.
* @function TabletProxy#isPathLoaded
- * @param {string} path
- * @returns {boolean}
+ * @param {string} path - The path to test.
+ * @returns {boolean} true
if path
is the current app or dialog, false
if it isn't.
*/
Q_INVOKABLE bool isPathLoaded(const QVariant& path);
@@ -384,44 +450,74 @@ public:
signals:
/**jsdoc
- * Signaled when this tablet receives an event from the html/js embedded in the tablet.
+ * Triggered when a message from the current HTML web page displayed on the tablet is received. The HTML web page can send
+ * a message by calling:
+ * EventBridge.emitWebEvent(message);
* @function TabletProxy#webEventReceived
- * @param {object|string} message
+ * @param {string|object} message - The message received.
* @returns {Signal}
*/
void webEventReceived(QVariant msg);
/**jsdoc
- * Signaled when this tablet receives an event from the qml embedded in the tablet.
+ * Triggered when a message from the current QML page displayed on the tablet is received. The QML page can send a message
+ * (string or object) by calling: sendToScript(message);
* @function TabletProxy#fromQml
- * @param {object|string} message
+ * @param {string|object} message - The message received.
* @returns {Signal}
*/
void fromQml(QVariant msg);
/**jsdoc
- * Signaled when this tablet screen changes.
+ * Triggered when the tablet's screen changes.
* @function TabletProxy#screenChanged
- * @param type {string} - "Home", "Web", "Menu", "QML", "Closed".
- * @param url {string} - Only valid for Web and QML.
+ * @param type {string} - The type of the new screen or change: "Home"
, "Menu"
,
+ * "QML"
, "Web"
, "Closed"
, or "Unknown"
.
+ * @param url {string} - The url of the page displayed. Only valid for Web and QML.
+ * @returns {Signal}
*/
void screenChanged(QVariant type, QVariant url);
/**jsdoc
- * Signaled when the tablet becomes visible or becomes invisible.
- * @function TabletProxy#isTabletShownChanged
+ * Triggered when the tablet is opened or closed.
+ * Note: Doesn't apply in toolbar mode.
+ * @function TabletProxy#tabletShownChanged
* @returns {Signal}
*/
void tabletShownChanged();
/**jsdoc
+ * Triggered when the tablet's toolbar mode changes.
* @function TabletProxy#toolbarModeChanged
+ * @returns {Signal}
+ * @example Report when the system tablet's toolbar mode changes.
+ * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ * tablet.toolbarModeChanged.connect(function () {
+ * print("Tablet toolbar mode changed to: " + tablet.toolbarMode);
+ * });
+ * // Use Developer > UI > Tablet Becomes Toolbar to change the toolbar mode.
*/
void toolbarModeChanged();
protected slots:
+
+ /**jsdoc
+ * @function TabletProxy#desktopWindowClosed
+ * @deprecated This function is deprecated and will be removed.
+ */
void desktopWindowClosed();
+
+ /**jsdoc
+ * @function TabletProxy#emitWebEvent
+ * @param {object|string} message - Message
+ * @deprecated This function is deprecated and will be removed.
+ */
void emitWebEvent(const QVariant& msg);
+
+ /**jsdoc
+ * @function TabletProxy#onTabletShown
+ * @deprecated This function is deprecated and will be removed.
+ */
void onTabletShown();
protected:
@@ -452,14 +548,20 @@ private:
Q_DECLARE_METATYPE(TabletProxy*);
/**jsdoc
+ * A tablet button. In toolbar mode (Developer > UI > Tablet Becomes Toolbar), the tablet button is displayed on the
+ * toolbar.
+ *
+ * Create a new button using {@link TabletProxy#addButton}.
+ *
* @class TabletButtonProxy
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
- * @property {Uuid} uuid - Uniquely identifies this button. Read-only.
- * @property {TabletButtonProxy.ButtonProperties} properties
+ * @property {Uuid} uuid - The ID of the button. Read-only.
+ * @property {TabletButtonProxy.ButtonProperties} properties - The current values of the button's properties. Only properties
+ * that have been set during button creation or subsequently edited are returned. Read-only.
*/
class TabletButtonProxy : public QObject {
Q_OBJECT
@@ -472,28 +574,66 @@ public:
QUuid getUuid() const { return _uuid; }
/**jsdoc
- * Returns the current value of this button's properties.
+ * Gets the current values of the button's properties. Only properties that have been set during button creation or
+ * subsequently edited are returned.
* @function TabletButtonProxy#getProperties
- * @returns {TabletButtonProxy.ButtonProperties}
+ * @returns {TabletButtonProxy.ButtonProperties} The button properties.
+ * @example Report a test button's properties.
+ * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ * var button = tablet.addButton({ text: "TEST" });
+ *
+ * var properties = button.getProperties();
+ * print("TEST button properties: " + JSON.stringify(properties));
+ *
+ * Script.scriptEnding.connect(function () {
+ * tablet.removeButton(button);
+ * });
*/
Q_INVOKABLE QVariantMap getProperties();
/**jsdoc
- * Replace the values of some of this button's properties.
+ * Changes the values of the button's properties.
* @function TabletButtonProxy#editProperties
- * @param {TabletButtonProxy.ButtonProperties} properties - Set of properties to change.
+ * @param {TabletButtonProxy.ButtonProperties} properties - The properties to change.
+ * @example Set a button's hover text after a delay.
+ * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ * var button = tablet.addButton({ text: "TEST" });
+ *
+ * button.propertiesChanged.connect(function () {
+ * print("TEST button properties changed");
+ * });
+ *
+ * Script.setTimeout(function () {
+ * button.editProperties({ text: "CHANGED" });
+ * }, 2000);
+ *
+ * Script.scriptEnding.connect(function () {
+ * tablet.removeButton(button);
+ * });
*/
Q_INVOKABLE void editProperties(const QVariantMap& properties);
signals:
/**jsdoc
- * Triggered when this button has been clicked on by the user.
+ * Triggered when the button is clicked.
* @function TabletButtonProxy#clicked
* @returns {Signal}
+ * @example Report a menu button click.
+ * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ * var button = tablet.addButton({ text: "TEST" });
+ *
+ * button.clicked.connect(function () {
+ * print("TEST button clicked");
+ * });
+ *
+ * Script.scriptEnding.connect(function () {
+ * tablet.removeButton(button);
+ * });
*/
void clicked();
/**jsdoc
+ * Triggered when a button's properties are changed.
* @function TabletButtonProxy#propertiesChanged
* @returns {Signal}
*/
@@ -503,20 +643,6 @@ protected:
QUuid _uuid;
int _stableOrder;
- /**jsdoc
- * @typedef {object} TabletButtonProxy.ButtonProperties
- * @property {string} icon - URL to button icon. (50 x 50)
- * @property {string} hoverIcon - URL to button icon, displayed during mouse hover. (50 x 50)
- * @property {string} activeHoverIcon - URL to button icon used when button is active, and during mouse hover. (50 x 50)
- * @property {string} activeIcon - URL to button icon used when button is active. (50 x 50)
- * @property {string} text - Button caption.
- * @property {string} hoverText - Button caption when button is not-active but during mouse hover.
- * @property {string} activeText - Button caption when button is active.
- * @property {string} activeHoverText - Button caption when button is active and during mouse hover.
- * @property {boolean} isActive - true
when button is active.
- * @property {number} sortOrder - Determines sort order on tablet. lower numbers will appear before larger numbers.
- * Default is 100.
- */
// FIXME: There are additional properties.
QVariantMap _properties;
};
From d0bd4a7d479a529b3bdcda9fe961dad6c9cd0079 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly
Date: Thu, 6 Jun 2019 14:01:34 -0700
Subject: [PATCH 006/106] try rebinding on error writing/reading from bound
socket
---
libraries/networking/src/udt/Socket.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp
index 2867e83a1c..c73ac8109f 100644
--- a/libraries/networking/src/udt/Socket.cpp
+++ b/libraries/networking/src/udt/Socket.cpp
@@ -500,10 +500,18 @@ std::vector Socket::getConnectionSockAddrs() {
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << socketError << "(" << _udpSocket.errorString() << ")";
+#ifdef WIN32
+ int wsaError = WSAGetLastError();
+ qCDebug(networking) << "windows socket error " << wsaError;
+#endif
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
+
+ if (_udpSocket.state() == QAbstractSocket::BoundState) {
+ rebind();
+ }
}
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
From d406e52d719debcf66002fffec4519232f8fd8bd Mon Sep 17 00:00:00 2001
From: Sam Gateau
Date: Thu, 6 Jun 2019 16:37:49 -0700
Subject: [PATCH 007/106] Messing around
---
interface/src/PerformanceManager.cpp | 3 +++
scripts/developer/utilities/render/lodWindow.js | 5 +++++
2 files changed, 8 insertions(+)
create mode 100644 scripts/developer/utilities/render/lodWindow.js
diff --git a/interface/src/PerformanceManager.cpp b/interface/src/PerformanceManager.cpp
index 874d92521b..910f42ac6e 100644
--- a/interface/src/PerformanceManager.cpp
+++ b/interface/src/PerformanceManager.cpp
@@ -13,6 +13,7 @@
#include
#include "scripting/RenderScriptingInterface.h"
+#include "LODManager.h"
PerformanceManager::PerformanceManager()
{
@@ -68,6 +69,8 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
+ // DependencyManager::get()->setWorldDetailQuality()
+
break;
case PerformancePreset::MID:
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::DEFERRED);
diff --git a/scripts/developer/utilities/render/lodWindow.js b/scripts/developer/utilities/render/lodWindow.js
new file mode 100644
index 0000000000..2f6b403a48
--- /dev/null
+++ b/scripts/developer/utilities/render/lodWindow.js
@@ -0,0 +1,5 @@
+var window = Desktop.createWindow(Script.resolvePath('./lod.qml'), {
+ title: "LOD Setup",
+ presentationMode: Desktop.PresentationMode.NATIVE,
+ size: {x: 350, y: 500}
+});
From 15d3c7231eabd8b50000499305e05abda2f8e923 Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Fri, 7 Jun 2019 12:21:32 +1200
Subject: [PATCH 008/106] Fix typos
---
libraries/ui/src/ui/TabletScriptingInterface.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index 4e639d221f..98a7f054ac 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -134,7 +134,7 @@ signals:
/**jsdoc
* Triggered when a tablet message or dialog is displayed on the tablet that needs the user's attention.
* Note: Only triggered if the script is running in the same script engine as the script that created
- * the tablet. Be default, this means in scripts included as part of the default scripts.
+ * the tablet. By default, this means in scripts included as part of the default scripts.
* @function Tablet.tabletNotification
* @returns {Signal}
*/
@@ -287,7 +287,7 @@ public:
* Opens a QML app or dialog on the tablet.
* @function TabletProxy#loadQMLSource
* @param {string} path - The path of the QML app or dialog.
- * @param {boolean} [resizable=false] - true
to make the dialog resizable in toolbar mode, false
+ * @param {boolean} [resizable=false] - true
to make the dialog resizable in toolbar mode, false
* to have it not resizable.
*/
Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false);
From 76836ebe9fab7786e1af05fee9906e0a0967bccf Mon Sep 17 00:00:00 2001
From: Sam Gateau
Date: Fri, 7 Jun 2019 09:32:26 -0700
Subject: [PATCH 009/106] Exposing a lot more details about displays on mac
---
.../src/platform/backend/MACOSPlatform.cpp | 111 +++++++++++++++++-
.../src/platform/backend/MACOSPlatform.h | 1 +
.../src/platform/backend/PlatformInstance.cpp | 1 +
.../src/platform/backend/PlatformInstance.h | 1 +
4 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/libraries/platform/src/platform/backend/MACOSPlatform.cpp b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
index 2607c47d5b..c7ab7c77a0 100644
--- a/libraries/platform/src/platform/backend/MACOSPlatform.cpp
+++ b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
@@ -18,6 +18,9 @@
#include
#include
#include
+
+#include
+#include
#endif
using namespace platform;
@@ -33,18 +36,124 @@ void MACOSInstance::enumerateCpu() {
}
void MACOSInstance::enumerateGpu() {
+#ifdef Q_OS_MAC
+/* uint32_t cglDisplayMask = -1; // Iterate over all of them.
+ CGLRendererInfoObj rendererInfo;
+ GLint rendererInfoCount;
+ CGLError err = CGLQueryRendererInfo(cglDisplayMask, &rendererInfo, &rendererInfoCount);
+ GLint j, numRenderers = 0, deviceVRAM, bestVRAM = 0;
+ err = CGLQueryRendererInfo(cglDisplayMask, &rendererInfo, &numRenderers);
+ if (0 == err) {
+ // Iterate over all of them and use the figure for the one with the most VRAM,
+ // on the assumption that this is the one that will actually be used.
+ CGLDescribeRenderer(rendererInfo, 0, kCGLRPRendererCount, &numRenderers);
+ for (j = 0; j < numRenderers; j++) {
+ CGLDescribeRenderer(rendererInfo, j, kCGLRPVideoMemoryMegabytes, &deviceVRAM);
+ if (deviceVRAM > bestVRAM) {
+ bestVRAM = deviceVRAM;
+ isValid = true;
+ }
+ }
+ }
+
+ //get gpu name
+ FILE* stream = popen("system_profiler SPDisplaysDataType | grep Chipset", "r");
+
+ std::ostringstream hostStream;
+ while (!feof(stream) && !ferror(stream)) {
+ char buf[128];
+ int bytesRead = fread(buf, 1, 128, stream);
+ hostStream.write(buf, bytesRead);
+ }
+
+ QString result = QString::fromStdString(hostStream.str());
+ QStringList parts = result.split('\n');
+ std::string name;
+
+ for (int i = 0; i < parts.size(); ++i) {
+ if (parts[i].toLower().contains("radeon") || parts[i].toLower().contains("nvidia")) {
+ _name=parts[i];
+ }
+ }
+
+ _dedicatedMemoryMB = bestVRAM;
+ CGLDestroyRendererInfo(rendererInfo);
+*/
+
+ // auto displayID = CGMainDisplayID();
+ // auto displayGLMask = CGDisplayIDToOpenGLDisplayMask(displayID);
+ // auto metalDevice = CGDirectDisplayCopyCurrentMetalDevice(displayID);
+
GPUIdent* ident = GPUIdent::getInstance();
json gpu = {};
+
gpu[keys::gpu::vendor] = ident->getName().toUtf8().constData();
gpu[keys::gpu::model] = ident->getName().toUtf8().constData();
gpu[keys::gpu::videoMemory] = ident->getMemory();
gpu[keys::gpu::driver] = ident->getDriver().toUtf8().constData();
_gpu.push_back(gpu);
- _display = ident->getOutput();
+
+#endif
}
+void MACOSInstance::enumerateDisplays() {
+#ifdef Q_OS_MAC
+ auto displayID = CGMainDisplayID();
+ auto displaySize = CGDisplayScreenSize(displayID);
+ auto displaySizeWidthInches = displaySize.width * 0.0393701;
+ auto displaySizeHeightInches = displaySize.height * 0.0393701;
+ auto displaySizeDiagonalInches = sqrt(displaySizeWidthInches * displaySizeWidthInches + displaySizeHeightInches * displaySizeHeightInches);
+
+ auto displayPixelsWidth= CGDisplayPixelsWide(displayID);
+ auto displayPixelsHeight= CGDisplayPixelsHigh(displayID);
+ auto displayBounds = CGDisplayBounds(displayID);
+ auto displayMaster =CGDisplayIsMain(displayID);
+
+ auto displayUnit =CGDisplayUnitNumber(displayID);
+ auto displayModel =CGDisplayModelNumber(displayID);
+ auto displayVendor = CGDisplayVendorNumber(displayID);
+ auto displaySerial = CGDisplaySerialNumber(displayID);
+
+ auto displayMode = CGDisplayCopyDisplayMode(displayID);
+ auto displayModeWidth = CGDisplayModeGetPixelWidth(displayMode);
+ auto displayModeHeight = CGDisplayModeGetPixelHeight(displayMode);
+ auto displayRefreshrate = CGDisplayModeGetRefreshRate(displayMode);
+
+ CGDisplayModeRelease(displayMode);
+
+ json display = {};
+
+ // display["physicalWidth"] = displaySizeWidthInches;
+ // display["physicalHeight"] = displaySizeHeightInches;
+ display["physicalDiagonal"] = displaySizeDiagonalInches;
+
+ // display["ppiH"] = displayModeWidth / displaySizeWidthInches;
+ // display["ppiV"] = displayModeHeight / displaySizeHeightInches;
+ display["ppi"] = sqrt(displayModeHeight * displayModeHeight + displayModeWidth * displayModeWidth) / displaySizeDiagonalInches;
+
+ display["coordLeft"] = displayBounds.origin.x;
+ display["coordRight"] = displayBounds.origin.x + displayBounds.size.width;
+ display["coordTop"] = displayBounds.origin.y;
+ display["coordBottom"] = displayBounds.origin.y + displayBounds.size.height;
+
+ display["isMaster"] = displayMaster;
+
+ display["unit"] = displayUnit;
+ display["vendor"] = displayVendor;
+ display["model"] = displayModel;
+ display["serial"] = displaySerial;
+
+ display["refreshrate"] =displayRefreshrate;
+ display["modeWidth"] = displayModeWidth;
+ display["modeHeight"] = displayModeHeight;
+
+
+ _display.push_back(display);
+#endif
+}
+
void MACOSInstance::enumerateMemory() {
json ram = {};
diff --git a/libraries/platform/src/platform/backend/MACOSPlatform.h b/libraries/platform/src/platform/backend/MACOSPlatform.h
index 1c66f5d742..4a257d8be5 100644
--- a/libraries/platform/src/platform/backend/MACOSPlatform.h
+++ b/libraries/platform/src/platform/backend/MACOSPlatform.h
@@ -18,6 +18,7 @@ namespace platform {
void enumerateCpu() override;
void enumerateMemory() override;
void enumerateGpu() override;
+ void enumerateDisplays() override;
void enumerateComputer () override;
};
diff --git a/libraries/platform/src/platform/backend/PlatformInstance.cpp b/libraries/platform/src/platform/backend/PlatformInstance.cpp
index 164fdb924f..3dd3e5f592 100644
--- a/libraries/platform/src/platform/backend/PlatformInstance.cpp
+++ b/libraries/platform/src/platform/backend/PlatformInstance.cpp
@@ -18,6 +18,7 @@ bool Instance::enumeratePlatform() {
enumerateComputer();
enumerateCpu();
enumerateGpu();
+ enumerateDisplays();
enumerateMemory();
// And profile the platform and put the tier in "computer"
diff --git a/libraries/platform/src/platform/backend/PlatformInstance.h b/libraries/platform/src/platform/backend/PlatformInstance.h
index 52fa9ec3f2..95eb2ef25e 100644
--- a/libraries/platform/src/platform/backend/PlatformInstance.h
+++ b/libraries/platform/src/platform/backend/PlatformInstance.h
@@ -37,6 +37,7 @@ public:
void virtual enumerateCpu()=0;
void virtual enumerateMemory()=0;
void virtual enumerateGpu()=0;
+ void virtual enumerateDisplays() {}
void virtual enumerateComputer()=0;
virtual ~Instance();
From 00baaffb97ebd66fb5179cca8da8c8a7d50c7a09 Mon Sep 17 00:00:00 2001
From: dante ruiz
Date: Fri, 7 Jun 2019 10:56:44 -0700
Subject: [PATCH 010/106] allow launcher to work from mounted dmg
---
launchers/darwin/src/DownloadInterface.m | 2 ++
launchers/darwin/src/Launcher.h | 1 +
launchers/darwin/src/Launcher.m | 11 +++++++----
launchers/darwin/src/main.mm | 2 +-
4 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/launchers/darwin/src/DownloadInterface.m b/launchers/darwin/src/DownloadInterface.m
index b957197567..ed7aaf51c4 100644
--- a/launchers/darwin/src/DownloadInterface.m
+++ b/launchers/darwin/src/DownloadInterface.m
@@ -46,6 +46,8 @@
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
NSLog(@"extract interface zip");
+ NSLog(@"%@", appPath);
+ NSLog(@"%@", [appPath stringByAppendingString:downloadFileName]);
[sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
NSLog(@"finished extracting interface zip");
diff --git a/launchers/darwin/src/Launcher.h b/launchers/darwin/src/Launcher.h
index 5838e25a82..69484a378d 100644
--- a/launchers/darwin/src/Launcher.h
+++ b/launchers/darwin/src/Launcher.h
@@ -57,6 +57,7 @@ typedef enum LoginErrorTypes
- (BOOL) loginShouldSetErrorState;
- (void) displayErrorPage;
- (void) showLoginScreen;
+- (NSString*) getLauncherPath;
- (ProcessState) currentProccessState;
- (void) setCurrentProcessState:(ProcessState) aProcessState;
- (void) setLoginErrorState:(LoginError) aLoginError;
diff --git a/launchers/darwin/src/Launcher.m b/launchers/darwin/src/Launcher.m
index 07fc8878da..3ab4379e7a 100644
--- a/launchers/darwin/src/Launcher.m
+++ b/launchers/darwin/src/Launcher.m
@@ -73,6 +73,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
return filePath;
}
+- (NSString*) getLauncherPath
+{
+ return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
+}
+
- (void) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
{
NSTask* task = [[NSTask alloc] init];
@@ -174,7 +179,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (NSString*) getAppPath
{
- return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
+ return [self getDownloadPathForContentAndScripts];
}
- (BOOL) loginShouldSetErrorState
@@ -317,7 +322,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (void) launchInterface
{
- NSString* launcherPath = [[self getAppPath] stringByAppendingString:@"HQ Launcher"];
+ NSString* launcherPath = [[self getLauncherPath] stringByAppendingString:@"HQ Launcher"];
[[Settings sharedSettings] setLauncherPath:launcherPath];
[[Settings sharedSettings] save];
@@ -351,8 +356,6 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
}
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
- //NSLog(@"arguments %@", [NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments]);
-
[NSApp terminate:self];
}
diff --git a/launchers/darwin/src/main.mm b/launchers/darwin/src/main.mm
index cdcd66f963..41e2986af7 100644
--- a/launchers/darwin/src/main.mm
+++ b/launchers/darwin/src/main.mm
@@ -17,7 +17,7 @@ void redirectLogToDocuments()
int main(int argc, const char* argv[]) {
//NSApp.appearance = [NSAppearance appearanceNamed: NSAppearanceNameAqua];
- redirectLogToDocuments();
+ //redirectLogToDocuments();
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.highfidelity.launcher"];
if ([apps count] > 1) {
NSLog(@"launcher is already running");
From 5499da0b7b5bad88b8075ef348916eb0d2d7d685 Mon Sep 17 00:00:00 2001
From: dante ruiz
Date: Fri, 7 Jun 2019 10:59:17 -0700
Subject: [PATCH 011/106] removing debug statments
---
launchers/darwin/src/DownloadInterface.m | 2 --
launchers/darwin/src/main.mm | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/launchers/darwin/src/DownloadInterface.m b/launchers/darwin/src/DownloadInterface.m
index ed7aaf51c4..b957197567 100644
--- a/launchers/darwin/src/DownloadInterface.m
+++ b/launchers/darwin/src/DownloadInterface.m
@@ -46,8 +46,6 @@
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
NSLog(@"extract interface zip");
- NSLog(@"%@", appPath);
- NSLog(@"%@", [appPath stringByAppendingString:downloadFileName]);
[sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
NSLog(@"finished extracting interface zip");
diff --git a/launchers/darwin/src/main.mm b/launchers/darwin/src/main.mm
index 41e2986af7..cdcd66f963 100644
--- a/launchers/darwin/src/main.mm
+++ b/launchers/darwin/src/main.mm
@@ -17,7 +17,7 @@ void redirectLogToDocuments()
int main(int argc, const char* argv[]) {
//NSApp.appearance = [NSAppearance appearanceNamed: NSAppearanceNameAqua];
- //redirectLogToDocuments();
+ redirectLogToDocuments();
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.highfidelity.launcher"];
if ([apps count] > 1) {
NSLog(@"launcher is already running");
From 6643633400e2049c1699f84272985872fe8c4e14 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly
Date: Fri, 7 Jun 2019 13:38:50 -0700
Subject: [PATCH 012/106] Move rebind on network error to rebind on silent
domain checkin
---
libraries/networking/src/NodeList.cpp | 6 ++++++
libraries/networking/src/udt/Socket.cpp | 4 ----
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 038d656dbb..0e6b5503d7 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -438,7 +438,13 @@ void NodeList::sendDomainServerCheckIn() {
// Send duplicate check-ins in the exponentially increasing sequence 1, 1, 2, 4, ...
static const int MAX_CHECKINS_TOGETHER = 20;
+ static const int REBIND_CHECKIN_COUNT = 2;
int outstandingCheckins = _domainHandler.getCheckInPacketsSinceLastReply();
+
+ if (outstandingCheckins > REBIND_CHECKIN_COUNT) {
+ _nodeSocket.rebind();
+ }
+
int checkinCount = outstandingCheckins > 1 ? std::pow(2, outstandingCheckins - 2) : 1;
checkinCount = std::min(checkinCount, MAX_CHECKINS_TOGETHER);
for (int i = 1; i < checkinCount; ++i) {
diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp
index c73ac8109f..0abe1eb172 100644
--- a/libraries/networking/src/udt/Socket.cpp
+++ b/libraries/networking/src/udt/Socket.cpp
@@ -508,10 +508,6 @@ void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
-
- if (_udpSocket.state() == QAbstractSocket::BoundState) {
- rebind();
- }
}
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
From ec1118db1fd26407c055d8e3956ae17303353a03 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly
Date: Fri, 7 Jun 2019 15:23:06 -0700
Subject: [PATCH 013/106] Attempting to write to a socket in unbound state was
causing qt to crash after a number of tries. This fix does an initial check
to prevent that case.
---
libraries/networking/src/udt/Socket.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp
index 0abe1eb172..406c2ff213 100644
--- a/libraries/networking/src/udt/Socket.cpp
+++ b/libraries/networking/src/udt/Socket.cpp
@@ -223,6 +223,13 @@ qint64 Socket::writeDatagram(const char* data, qint64 size, const HifiSockAddr&
qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr) {
+ // don't attempt to write the datagram if we're unbound. Just drop it.
+ // _udpSocket.writeDatagram will return an error anyway, but there are
+ // potential crashes in Qt when that happens.
+ if (_udpSocket.state() != QAbstractSocket::BoundState) {
+ qCDebug(networking) << "Attempt to writeDatagram when in unbound state";
+ return -1;
+ }
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
if (bytesWritten < 0) {
From 22d602a33406477d3c312f9ce8fe3cc725b32e3e Mon Sep 17 00:00:00 2001
From: Seth Alves
Date: Fri, 7 Jun 2019 14:39:53 -0700
Subject: [PATCH 014/106] CREATE app will now recognize newer-style baked
models and skyboxes as baked in entity-list
---
scripts/system/libraries/entityList.js | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js
index d19da2b342..6498c92f17 100644
--- a/scripts/system/libraries/entityList.js
+++ b/scripts/system/libraries/entityList.js
@@ -10,7 +10,7 @@
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible,
- keyUpEventFromUIWindow */
+ keyUpEventFromUIWindow, Script, SelectionDisplay, SelectionManager, Clipboard */
var PROFILING_ENABLED = false;
var profileIndent = '';
@@ -148,6 +148,20 @@ EntityListTool = function(shouldUseEditTabletApp) {
return value !== undefined ? value : "";
}
+ function entityIsBaked(properties) {
+ if (properties.type === "Model") {
+ var lowerModelURL = properties.modelURL.toLowerCase();
+ return lowerModelURL.endsWith(".baked.fbx") || lowerModelURL.endsWith(".baked.fst");
+ } else if (properties.type === "Zone") {
+ var lowerSkyboxURL = properties.skybox ? properties.skybox.url.toLowerCase() : "";
+ var lowerAmbientURL = properties.ambientLight ? properties.ambientLight.ambientURL.toLowerCase() : "";
+ return (lowerSkyboxURL === "" || lowerSkyboxURL.endsWith(".texmeta.json")) &&
+ (lowerAmbientURL === "" || lowerAmbientURL.endsWith(".texmeta.json"));
+ } else {
+ return false;
+ }
+ }
+
that.sendUpdate = function() {
PROFILE('Script-sendUpdate', function() {
var entities = [];
@@ -164,7 +178,8 @@ EntityListTool = function(shouldUseEditTabletApp) {
var cameraPosition = Camera.position;
PROFILE("getMultipleProperties", function () {
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked',
- 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID']);
+ 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
+ 'skybox.url', 'ambientLight.url']);
for (var i = 0; i < multipleProperties.length; i++) {
var properties = multipleProperties[i];
@@ -193,7 +208,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
valueIfDefined(properties.renderInfo.texturesSize) : ""),
hasTransparent: (properties.renderInfo !== undefined ?
valueIfDefined(properties.renderInfo.hasTransparent) : ""),
- isBaked: properties.type === "Model" ? url.toLowerCase().endsWith(".baked.fbx") : false,
+ isBaked: entityIsBaked(properties),
drawCalls: (properties.renderInfo !== undefined ?
valueIfDefined(properties.renderInfo.drawCalls) : ""),
hasScript: properties.script !== ""
From 2d9a7f7c4f5db71ea7b9b555ca87d621cc38baa6 Mon Sep 17 00:00:00 2001
From: Sam Gateau
Date: Fri, 7 Jun 2019 17:00:37 -0700
Subject: [PATCH 015/106] Refined the capping of the LOD Target Rate to be the
lower REfreshrate when FOcus-inactive, this prevent the panic attacks of the
LOD system on low end hardware
---
.../qml/+android_interface/Stats.qml | 3 +
interface/resources/qml/Stats.qml | 61 ++++++++++---------
interface/src/LODManager.cpp | 13 +++-
interface/src/PerformanceManager.cpp | 5 +-
interface/src/RefreshRateManager.cpp | 23 ++++---
interface/src/RefreshRateManager.h | 3 +
interface/src/ui/Stats.cpp | 2 +
interface/src/ui/Stats.h | 18 ++++++
.../src/platform/backend/MACOSPlatform.cpp | 15 +++--
9 files changed, 94 insertions(+), 49 deletions(-)
diff --git a/interface/resources/qml/+android_interface/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml
index 54f6086a86..1f07af786f 100644
--- a/interface/resources/qml/+android_interface/Stats.qml
+++ b/interface/resources/qml/+android_interface/Stats.qml
@@ -269,6 +269,9 @@ Item {
StatText {
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
+ StatText {
+ text: "LOD Target: " + root.lodTargetFramerate + " Hz Angle: " + root.lodAngle + " deg"
+ }
StatText {
text: "Drawcalls: " + root.drawcalls
}
diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml
index 5af3ba9168..2c991aa9dd 100644
--- a/interface/resources/qml/Stats.qml
+++ b/interface/resources/qml/Stats.qml
@@ -259,6 +259,35 @@ Item {
visible: root.expanded;
text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps";
}
+ StatText {
+ visible: !root.expanded
+ text: "Octree Elements Server: " + root.serverElements +
+ " Local: " + root.localElements;
+ }
+ StatText {
+ visible: root.expanded
+ text: "Octree Sending Mode: " + root.sendingMode;
+ }
+ StatText {
+ visible: root.expanded
+ text: "Octree Packets to Process: " + root.packetStats;
+ }
+ StatText {
+ visible: root.expanded
+ text: "Octree Elements - ";
+ }
+ StatText {
+ visible: root.expanded
+ text: "\tServer: " + root.serverElements +
+ " Internal: " + root.serverInternal +
+ " Leaves: " + root.serverLeaves;
+ }
+ StatText {
+ visible: root.expanded
+ text: "\tLocal: " + root.localElements +
+ " Internal: " + root.localInternal +
+ " Leaves: " + root.localLeaves;
+ }
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
@@ -316,6 +345,9 @@ Item {
StatText {
text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y
}
+ StatText {
+ text: "LOD Target: " + root.lodTargetFramerate + " Hz Angle: " + root.lodAngle + " deg"
+ }
StatText {
text: "Drawcalls: " + root.drawcalls
}
@@ -401,35 +433,6 @@ Item {
text: " out of view: " + root.shadowOutOfView +
" too small: " + root.shadowTooSmall;
}
- StatText {
- visible: !root.expanded
- text: "Octree Elements Server: " + root.serverElements +
- " Local: " + root.localElements;
- }
- StatText {
- visible: root.expanded
- text: "Octree Sending Mode: " + root.sendingMode;
- }
- StatText {
- visible: root.expanded
- text: "Octree Packets to Process: " + root.packetStats;
- }
- StatText {
- visible: root.expanded
- text: "Octree Elements - ";
- }
- StatText {
- visible: root.expanded
- text: "\tServer: " + root.serverElements +
- " Internal: " + root.serverInternal +
- " Leaves: " + root.serverLeaves;
- }
- StatText {
- visible: root.expanded
- text: "\tLocal: " + root.localElements +
- " Internal: " + root.localInternal +
- " Leaves: " + root.localLeaves;
- }
StatText {
visible: root.expanded
text: "LOD: " + root.lodStatus;
diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp
index b0b919fb0f..0c9587d3ae 100644
--- a/interface/src/LODManager.cpp
+++ b/interface/src/LODManager.cpp
@@ -351,7 +351,18 @@ float LODManager::getHMDLODTargetFPS() const {
}
float LODManager::getLODTargetFPS() const {
- auto refreshRateFPS = qApp->getRefreshRateManager().getActiveRefreshRate();
+
+ // Use the current refresh rate as the recommended rate target used to cap the LOD manager control value.
+ // When focused, Use the Focus Inactive as the targget LOD to void abrupt changes from the lod controller.
+ auto& refreshRateManager = qApp->getRefreshRateManager();
+ auto refreshRateRegime = refreshRateManager.getRefreshRateRegime();
+ auto refreshRateProfile = refreshRateManager.getRefreshRateProfile();
+ auto refreshRateUXMode = refreshRateManager.getUXMode();
+ auto refreshRateFPS = refreshRateManager.getActiveRefreshRate();
+ if (refreshRateRegime == RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE) {
+ refreshRateFPS = refreshRateManager.queryRefreshRateTarget(refreshRateProfile, RefreshRateManager::RefreshRateRegime::FOCUS_INACTIVE, refreshRateUXMode);
+ }
+
auto lodTargetFPS = getDesktopLODTargetFPS();
if (qApp->isHMDMode()) {
lodTargetFPS = getHMDLODTargetFPS();
diff --git a/interface/src/PerformanceManager.cpp b/interface/src/PerformanceManager.cpp
index 910f42ac6e..cf6da11aeb 100644
--- a/interface/src/PerformanceManager.cpp
+++ b/interface/src/PerformanceManager.cpp
@@ -69,13 +69,14 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
- // DependencyManager::get()->setWorldDetailQuality()
+ DependencyManager::get()->setWorldDetailQuality(0.5f);
break;
case PerformancePreset::MID:
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::DEFERRED);
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE);
+ DependencyManager::get()->setWorldDetailQuality(0.5f);
break;
case PerformancePreset::LOW:
@@ -83,6 +84,8 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO);
+ DependencyManager::get()->setWorldDetailQuality(0.75f);
+
break;
case PerformancePreset::UNKNOWN:
default:
diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp
index 4963eee8cf..922682d9c0 100644
--- a/interface/src/RefreshRateManager.cpp
+++ b/interface/src/RefreshRateManager.cpp
@@ -107,9 +107,7 @@ RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile
RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME;
if (getUXMode() != RefreshRateManager::UXMode::VR) {
- profile =(RefreshRateManager::RefreshRateProfile) _refreshRateProfileSettingLock.resultWithReadLock([&] {
- return _refreshRateProfileSetting.get();
- });
+ return _refreshRateProfile;
}
return profile;
@@ -138,15 +136,20 @@ void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
}
}
+int RefreshRateManager::queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const {
+ int targetRefreshRate;
+ if (uxMode == RefreshRateManager::UXMode::DESKTOP) {
+ targetRefreshRate = REFRESH_RATE_PROFILES[profile][regime];
+ }
+ else {
+ targetRefreshRate = VR_TARGET_RATE;
+ }
+ return targetRefreshRate;
+}
+
void RefreshRateManager::updateRefreshRateController() const {
if (_refreshRateOperator) {
- int targetRefreshRate;
- if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
- targetRefreshRate = REFRESH_RATE_PROFILES[_refreshRateProfile][_refreshRateRegime];
- } else {
- targetRefreshRate = VR_TARGET_RATE;
- }
-
+ int targetRefreshRate = queryRefreshRateTarget(_refreshRateProfile, _refreshRateRegime, _uxMode);
_refreshRateOperator(targetRefreshRate);
_activeRefreshRate = targetRefreshRate;
}
diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h
index 74dd8156e1..567a515898 100644
--- a/interface/src/RefreshRateManager.h
+++ b/interface/src/RefreshRateManager.h
@@ -65,6 +65,9 @@ public:
int getActiveRefreshRate() const { return _activeRefreshRate; }
void updateRefreshRateController() const;
+ // query the refresh rate target at the specified combination
+ int queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const;
+
void resetInactiveTimer();
void toggleInactive();
diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index d243fa9ebf..dc9780adf5 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -416,6 +416,8 @@ void Stats::updateStats(bool force) {
gpuContext->getFrameStats(gpuFrameStats);
STAT_UPDATE(drawcalls, gpuFrameStats._DSNumDrawcalls);
+ STAT_UPDATE(lodTargetFramerate, DependencyManager::get()->getLODTargetFPS());
+ STAT_UPDATE(lodAngle, DependencyManager::get()->getLODAngleDeg());
// Incoming packets
diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h
index cb13945320..ef4668f85e 100644
--- a/interface/src/ui/Stats.h
+++ b/interface/src/ui/Stats.h
@@ -109,6 +109,8 @@ private: \
* @property {number} shadowRendered - Read-only.
* @property {string} sendingMode - Read-only.
* @property {string} packetStats - Read-only.
+ * @property {number} lodAngle - Read-only.
+ * @property {number} lodTargetFramerate - Read-only.
* @property {string} lodStatus - Read-only.
* @property {string} timingStats - Read-only.
* @property {string} gameUpdateStats - Read-only.
@@ -272,6 +274,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, shadowRendered, 0)
STATS_PROPERTY(QString, sendingMode, QString())
STATS_PROPERTY(QString, packetStats, QString())
+ STATS_PROPERTY(int, lodAngle, 0)
+ STATS_PROPERTY(int, lodTargetFramerate, 0)
STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, gameUpdateStats, QString())
@@ -858,6 +862,20 @@ signals:
*/
void packetStatsChanged();
+ /**jsdoc
+ * Triggered when the value of the lodAngle
property changes.
+ * @function Stats.lodAngleChanged
+ * @returns {Signal}
+ */
+ void lodAngleChanged();
+
+ /**jsdoc
+ * Triggered when the value of the lodTargetFramerate
property changes.
+ * @function Stats.lodTargetFramerateChanged
+ * @returns {Signal}
+ */
+ void lodTargetFramerateChanged();
+
/**jsdoc
* Triggered when the value of the lodStatus
property changes.
* @function Stats.lodStatusChanged
diff --git a/libraries/platform/src/platform/backend/MACOSPlatform.cpp b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
index c7ab7c77a0..695c872e90 100644
--- a/libraries/platform/src/platform/backend/MACOSPlatform.cpp
+++ b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
@@ -102,8 +102,10 @@ void MACOSInstance::enumerateDisplays() {
#ifdef Q_OS_MAC
auto displayID = CGMainDisplayID();
auto displaySize = CGDisplayScreenSize(displayID);
- auto displaySizeWidthInches = displaySize.width * 0.0393701;
- auto displaySizeHeightInches = displaySize.height * 0.0393701;
+
+ const auto MM_TO_IN = 0.0393701;
+ auto displaySizeWidthInches = displaySize.width * MM_TO_IN;
+ auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
auto displaySizeDiagonalInches = sqrt(displaySizeWidthInches * displaySizeWidthInches + displaySizeHeightInches * displaySizeHeightInches);
auto displayPixelsWidth= CGDisplayPixelsWide(displayID);
@@ -125,12 +127,10 @@ void MACOSInstance::enumerateDisplays() {
json display = {};
- // display["physicalWidth"] = displaySizeWidthInches;
- // display["physicalHeight"] = displaySizeHeightInches;
+ display["physicalWidth"] = displaySizeWidthInches;
+ display["physicalHeight"] = displaySizeHeightInches;
display["physicalDiagonal"] = displaySizeDiagonalInches;
- // display["ppiH"] = displayModeWidth / displaySizeWidthInches;
- // display["ppiV"] = displayModeHeight / displaySizeHeightInches;
display["ppi"] = sqrt(displayModeHeight * displayModeHeight + displayModeWidth * displayModeWidth) / displaySizeDiagonalInches;
display["coordLeft"] = displayBounds.origin.x;
@@ -148,8 +148,7 @@ void MACOSInstance::enumerateDisplays() {
display["refreshrate"] =displayRefreshrate;
display["modeWidth"] = displayModeWidth;
display["modeHeight"] = displayModeHeight;
-
-
+
_display.push_back(display);
#endif
}
From e0714e5b1a6033c8294d26118fc7cc7c6d71b7cc Mon Sep 17 00:00:00 2001
From: Sam Gateau
Date: Fri, 7 Jun 2019 17:18:30 -0700
Subject: [PATCH 016/106] Removing comments
---
.../src/platform/backend/MACOSPlatform.cpp | 50 +------------------
1 file changed, 1 insertion(+), 49 deletions(-)
diff --git a/libraries/platform/src/platform/backend/MACOSPlatform.cpp b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
index 695c872e90..7dbc403783 100644
--- a/libraries/platform/src/platform/backend/MACOSPlatform.cpp
+++ b/libraries/platform/src/platform/backend/MACOSPlatform.cpp
@@ -37,53 +37,7 @@ void MACOSInstance::enumerateCpu() {
void MACOSInstance::enumerateGpu() {
#ifdef Q_OS_MAC
-/* uint32_t cglDisplayMask = -1; // Iterate over all of them.
- CGLRendererInfoObj rendererInfo;
- GLint rendererInfoCount;
- CGLError err = CGLQueryRendererInfo(cglDisplayMask, &rendererInfo, &rendererInfoCount);
- GLint j, numRenderers = 0, deviceVRAM, bestVRAM = 0;
- err = CGLQueryRendererInfo(cglDisplayMask, &rendererInfo, &numRenderers);
- if (0 == err) {
- // Iterate over all of them and use the figure for the one with the most VRAM,
- // on the assumption that this is the one that will actually be used.
- CGLDescribeRenderer(rendererInfo, 0, kCGLRPRendererCount, &numRenderers);
- for (j = 0; j < numRenderers; j++) {
- CGLDescribeRenderer(rendererInfo, j, kCGLRPVideoMemoryMegabytes, &deviceVRAM);
- if (deviceVRAM > bestVRAM) {
- bestVRAM = deviceVRAM;
- isValid = true;
- }
- }
- }
-
- //get gpu name
- FILE* stream = popen("system_profiler SPDisplaysDataType | grep Chipset", "r");
-
- std::ostringstream hostStream;
- while (!feof(stream) && !ferror(stream)) {
- char buf[128];
- int bytesRead = fread(buf, 1, 128, stream);
- hostStream.write(buf, bytesRead);
- }
-
- QString result = QString::fromStdString(hostStream.str());
- QStringList parts = result.split('\n');
- std::string name;
-
- for (int i = 0; i < parts.size(); ++i) {
- if (parts[i].toLower().contains("radeon") || parts[i].toLower().contains("nvidia")) {
- _name=parts[i];
- }
- }
-
- _dedicatedMemoryMB = bestVRAM;
- CGLDestroyRendererInfo(rendererInfo);
-*/
-
- // auto displayID = CGMainDisplayID();
- // auto displayGLMask = CGDisplayIDToOpenGLDisplayMask(displayID);
- // auto metalDevice = CGDirectDisplayCopyCurrentMetalDevice(displayID);
-
+
GPUIdent* ident = GPUIdent::getInstance();
json gpu = {};
@@ -108,8 +62,6 @@ void MACOSInstance::enumerateDisplays() {
auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
auto displaySizeDiagonalInches = sqrt(displaySizeWidthInches * displaySizeWidthInches + displaySizeHeightInches * displaySizeHeightInches);
- auto displayPixelsWidth= CGDisplayPixelsWide(displayID);
- auto displayPixelsHeight= CGDisplayPixelsHigh(displayID);
auto displayBounds = CGDisplayBounds(displayID);
auto displayMaster =CGDisplayIsMain(displayID);
From 0b282b201cb80b32077d123aee156623783ae65f Mon Sep 17 00:00:00 2001
From: Anna
Date: Fri, 7 Jun 2019 18:01:47 -0700
Subject: [PATCH 017/106] typo fix
---
interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
index e824036587..0ca9d6d80c 100644
--- a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
@@ -22,7 +22,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.reslovedUrl(path));
+ profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {
From fd872009fe73a97d5f36890ddbedece5032d9145 Mon Sep 17 00:00:00 2001
From: Anna
Date: Mon, 10 Jun 2019 09:25:12 -0700
Subject: [PATCH 018/106] added workaround for bug in Qt StackView push method
---
interface/resources/qml/hifi/tablet/Edit.qml | 3 ++-
interface/resources/qml/hifi/tablet/EditTools.qml | 3 ++-
interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml | 3 ++-
.../resources/qml/hifi/tablet/TabletAvatarPreferences.qml | 3 ++-
.../resources/qml/hifi/tablet/TabletGeneralPreferences.qml | 3 ++-
.../resources/qml/hifi/tablet/TabletGraphicsPreferences.qml | 3 ++-
interface/resources/qml/hifi/tablet/TabletLodPreferences.qml | 3 ++-
.../resources/qml/hifi/tablet/TabletNetworkingPreferences.qml | 3 ++-
8 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml
index 099c53cda2..ca18388def 100644
--- a/interface/resources/qml/hifi/tablet/Edit.qml
+++ b/interface/resources/qml/hifi/tablet/Edit.qml
@@ -34,7 +34,8 @@ StackView {
}
function pushSource(path) {
- editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties,
+ var item = Qt.createComponent(Qt.resolvedUrl("../../" + path));
+ editRoot.push(item, itemProperties,
StackView.Immediate);
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
}
diff --git a/interface/resources/qml/hifi/tablet/EditTools.qml b/interface/resources/qml/hifi/tablet/EditTools.qml
index f989038c16..976e98cd50 100644
--- a/interface/resources/qml/hifi/tablet/EditTools.qml
+++ b/interface/resources/qml/hifi/tablet/EditTools.qml
@@ -37,7 +37,8 @@ StackView {
}
function pushSource(path) {
- editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties,
+ var item = Qt.createComponent(Qt.resolvedUrl("../../" + path));
+ editRoot.push(item, itemProperties,
StackView.Immediate);
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
}
diff --git a/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml
index 239c2452d4..2836c2bac6 100644
--- a/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml
+++ b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml
@@ -24,7 +24,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
diff --git a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
index 0ca9d6d80c..dbe4ab12db 100644
--- a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml
@@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml
index 4f1100f20b..254e4203ce 100644
--- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml
@@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
diff --git a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml
index 8d600975ed..9b9f01d031 100644
--- a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml
@@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
diff --git a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml
index ddc116371d..0938d0a82e 100644
--- a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml
@@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
diff --git a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml
index bad546a39c..67d6e6c52e 100644
--- a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml
+++ b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml
@@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
- profileRoot.push(Qt.resolvedUrl(path));
+ var item = Qt.createComponent(Qt.resolvedUrl(path));
+ profileRoot.push(item);
}
function popSource() {
From 4cad49f561f8f3437ee0810c81f511e617938f64 Mon Sep 17 00:00:00 2001
From: amer cerkic
Date: Mon, 10 Jun 2019 10:42:31 -0700
Subject: [PATCH 019/106] adding undefined check on the toolbar object
---
scripts/simplifiedUI/ui/simplifiedUI.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js
index cdf6a9591a..88197c0837 100644
--- a/scripts/simplifiedUI/ui/simplifiedUI.js
+++ b/scripts/simplifiedUI/ui/simplifiedUI.js
@@ -535,7 +535,9 @@ function shutdown() {
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
- toolbar.writeProperty("visible", true);
+ if (typeof toolbar !== 'undefined') {
+ toolbar.writeProperty("visible", true);
+ }
}
}
From 23f8778feb3bf6e1d60f6f67bd23df23991a8da3 Mon Sep 17 00:00:00 2001
From: SamGondelman
Date: Mon, 10 Jun 2019 11:10:29 -0700
Subject: [PATCH 020/106] possible fix for mac crash?
---
libraries/entities-renderer/src/RenderablePolyLineEntityItem.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
index fa6fff98d7..9139c260ea 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
@@ -55,7 +55,7 @@ protected:
bool _faceCamera { false };
bool _glow { false };
- size_t _numVertices;
+ size_t _numVertices { 0 };
gpu::BufferPointer _polylineDataBuffer;
gpu::BufferPointer _polylineGeometryBuffer;
static std::map, gpu::PipelinePointer> _pipelines;
From edc1018b2a42a7c5fd3f4df8e9ebcfe673c202c3 Mon Sep 17 00:00:00 2001
From: amer cerkic
Date: Mon, 10 Jun 2019 11:12:29 -0700
Subject: [PATCH 021/106] simplified the check
---
scripts/simplifiedUI/ui/simplifiedUI.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js
index 88197c0837..bafc5fffe1 100644
--- a/scripts/simplifiedUI/ui/simplifiedUI.js
+++ b/scripts/simplifiedUI/ui/simplifiedUI.js
@@ -535,7 +535,7 @@ function shutdown() {
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
- if (typeof toolbar !== 'undefined') {
+ if (toolbar) {
toolbar.writeProperty("visible", true);
}
}
From c39218c1f4c76aad309ed153301ef331639e5794 Mon Sep 17 00:00:00 2001
From: Sam Gateau
Date: Mon, 10 Jun 2019 11:59:09 -0700
Subject: [PATCH 022/106] Addressing the review comment about an if else
statement syntax
---
interface/src/RefreshRateManager.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp
index 922682d9c0..da6c5cbd37 100644
--- a/interface/src/RefreshRateManager.cpp
+++ b/interface/src/RefreshRateManager.cpp
@@ -137,13 +137,10 @@ void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
}
int RefreshRateManager::queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const {
- int targetRefreshRate;
+ int targetRefreshRate = VR_TARGET_RATE;
if (uxMode == RefreshRateManager::UXMode::DESKTOP) {
targetRefreshRate = REFRESH_RATE_PROFILES[profile][regime];
}
- else {
- targetRefreshRate = VR_TARGET_RATE;
- }
return targetRefreshRate;
}
From 9c6ca601087c6ea8331c022955020a4deea5631b Mon Sep 17 00:00:00 2001
From: Howard Stearns
Date: Mon, 10 Jun 2019 12:08:51 -0700
Subject: [PATCH 023/106] get windows error first, check for socket changes,
and fix bad Q_OS_WINDOWS ref
---
libraries/networking/src/udt/Socket.cpp | 26 ++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp
index 406c2ff213..6e27fd2483 100644
--- a/libraries/networking/src/udt/Socket.cpp
+++ b/libraries/networking/src/udt/Socket.cpp
@@ -59,10 +59,12 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
auto sd = _udpSocket.socketDescriptor();
int val = IP_PMTUDISC_DONT;
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
-#elif defined(Q_OS_WINDOWS)
+#elif defined(Q_OS_WIN)
auto sd = _udpSocket.socketDescriptor();
int val = 0; // false
- setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val));
+ if (setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, (const char *)&val, sizeof(val))) {
+ qCWarning(networking) << "Socket::bind Cannot setsockopt IP_DONTFRAGMENT" << WSAGetLastError();
+ }
#endif
}
}
@@ -231,14 +233,14 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
return -1;
}
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
-
- if (bytesWritten < 0) {
- qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << ") error - " << _udpSocket.error() << "(" << _udpSocket.errorString() << ")";
-
+ int pending = _udpSocket.bytesToWrite();
+ if (bytesWritten < 0 || pending) {
+ int wsaError = 0;
#ifdef WIN32
- int wsaError = WSAGetLastError();
- qCDebug(networking) << "windows socket error " << wsaError;
+ wsaError = WSAGetLastError();
#endif
+ qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << ") error - " << wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
+ << (pending ? "pending bytes:" : "pending:") << pending;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
@@ -506,11 +508,13 @@ std::vector Socket::getConnectionSockAddrs() {
}
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
- qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << socketError << "(" << _udpSocket.errorString() << ")";
+ int wsaError = 0;
#ifdef WIN32
- int wsaError = WSAGetLastError();
- qCDebug(networking) << "windows socket error " << wsaError;
+ wsaError = WSAGetLastError();
#endif
+ int pending = _udpSocket.bytesToWrite();
+ qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError << "(" << _udpSocket.errorString() << ")"
+ << (pending ? "pending bytes:" : "pending:") << pending;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
From a400cea0404dc5b936799031ee23e1d1271528e7 Mon Sep 17 00:00:00 2001
From: sabrina-shanman
Date: Mon, 10 Jun 2019 14:00:05 -0700
Subject: [PATCH 024/106] Fix crash when using null DisplayPlugin in
GraphicsEngine during shutdown
---
interface/src/graphics/GraphicsEngine.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp
index 4f8b86cc0c..7f9a612697 100644
--- a/interface/src/graphics/GraphicsEngine.cpp
+++ b/interface/src/graphics/GraphicsEngine.cpp
@@ -132,6 +132,10 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
bool GraphicsEngine::shouldPaint() const {
auto displayPlugin = qApp->getActiveDisplayPlugin();
+ if (!displayPlugin) {
+ // We're shutting down
+ return false;
+ }
#ifdef DEBUG_PAINT_DELAY
static uint64_t paintDelaySamples{ 0 };
@@ -175,6 +179,10 @@ void GraphicsEngine::render_performFrame() {
{
PROFILE_RANGE(render, "/getActiveDisplayPlugin");
displayPlugin = qApp->getActiveDisplayPlugin();
+ if (!displayPlugin) {
+ // We're shutting down
+ return;
+ }
}
{
From 67995a8677f1ac15eb5de9610a06de80ea394e46 Mon Sep 17 00:00:00 2001
From: Seth Alves
Date: Mon, 10 Jun 2019 14:25:44 -0700
Subject: [PATCH 025/106] don't divide by zero if a grid entity-item has a zero
dimension
---
libraries/render-utils/src/GeometryCache.cpp | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 7bd6f88d71..88cca1693b 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -939,6 +939,11 @@ 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, bool forward, int id) {
+
+ if (majorRows == 0 || majorCols == 0) {
+ return;
+ }
+
Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);
Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);
Vec2FloatPairPair key(majorKey, minorKey);
@@ -962,8 +967,8 @@ void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, co
gridBuffer.edit().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
gridBuffer.edit().offset.x = -(majorEdge / majorRows) / 2;
gridBuffer.edit().offset.y = -(majorEdge / majorCols) / 2;
- gridBuffer.edit().offset.z = -(minorEdge / minorRows) / 2;
- gridBuffer.edit().offset.w = -(minorEdge / minorCols) / 2;
+ gridBuffer.edit().offset.z = minorRows == 0 ? 0 : -(minorEdge / minorRows) / 2;
+ gridBuffer.edit().offset.w = minorCols == 0 ? 0 : -(minorEdge / minorCols) / 2;
gridBuffer.edit().edge = glm::vec4(glm::vec2(majorEdge),
// If rows or columns are not set, do not draw minor gridlines
glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));
From adeedc88d3c882255ba2428736bc098032a0fb68 Mon Sep 17 00:00:00 2001
From: dante ruiz
Date: Mon, 10 Jun 2019 14:38:01 -0700
Subject: [PATCH 026/106] add launcher error page
---
launchers/darwin/src/DownloadDomainContent.m | 2 +-
launchers/darwin/src/DownloadInterface.m | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/launchers/darwin/src/DownloadDomainContent.m b/launchers/darwin/src/DownloadDomainContent.m
index 7b0247a6f4..f0b625c3e2 100644
--- a/launchers/darwin/src/DownloadDomainContent.m
+++ b/launchers/darwin/src/DownloadDomainContent.m
@@ -54,7 +54,7 @@
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
+ [[Launcher sharedLauncher] displayErrorPage];
}
-
@end
diff --git a/launchers/darwin/src/DownloadInterface.m b/launchers/darwin/src/DownloadInterface.m
index b957197567..bcc07f6377 100644
--- a/launchers/darwin/src/DownloadInterface.m
+++ b/launchers/darwin/src/DownloadInterface.m
@@ -66,6 +66,7 @@
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
+ [[Launcher sharedLauncher] displayErrorPage];
}
From 654b5775e17bf14f0a5d6dad7d86927698ae833a Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault"
Date: Mon, 10 Jun 2019 15:05:34 -0700
Subject: [PATCH 027/106] Crash fix for ResourceCache::getResource()
A wrong 'read' locker is used around a critical section that actually modifies the _resources QHash.
This can result in memory corruption and seg faults.
---
libraries/networking/src/ResourceCache.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index 44d3d1ee4d..ef8a7b577d 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -346,7 +346,7 @@ void ResourceCache::setRequestLimit(uint32_t limit) {
QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash) {
QSharedPointer resource;
{
- QReadLocker locker(&_resourcesLock);
+ QWriteLocker locker(&_resourcesLock);
auto& resourcesWithExtraHash = _resources[url];
auto resourcesWithExtraHashIter = resourcesWithExtraHash.find(extraHash);
if (resourcesWithExtraHashIter != resourcesWithExtraHash.end()) {
From dbce9768873f57dd089cf2dcd069b7fb3131c6c7 Mon Sep 17 00:00:00 2001
From: Andrew Meadows
Date: Mon, 10 Jun 2019 15:34:18 -0700
Subject: [PATCH 028/106] fix another crash in
MyAvatar::getAvatarEntitiesVariant()
---
interface/src/avatar/MyAvatar.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index b91217da63..3800a330bb 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -2488,12 +2488,12 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() {
QVariantMap avatarEntityData;
avatarEntityData["id"] = entityID;
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
- QScriptValue scriptProperties;
{
std::lock_guard guard(_scriptEngineLock);
+ QScriptValue scriptProperties;
scriptProperties = EntityItemPropertiesToScriptValue(_scriptEngine, entityProperties);
+ avatarEntityData["properties"] = scriptProperties.toVariant();
}
- avatarEntityData["properties"] = scriptProperties.toVariant();
avatarEntitiesData.append(QVariant(avatarEntityData));
}
}
From 7ce5f7ac0b3a4bbd172bf17419d8de4b130478fc Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault"
Date: Mon, 10 Jun 2019 16:27:03 -0700
Subject: [PATCH 029/106] Adjust run and jog anims to better align head with 1p
camera
Added adjust forward run and jog animations to better align with first person camera.
In some instances, such as looking down, you could sometimes see your body lead your camera.
https://highfidelity.atlassian.net/browse/BUGZ-240
---
.../resources/avatar/animations/jog_fwd.fbx | Bin 892208 -> 928272 bytes
.../resources/avatar/animations/run_fwd.fbx | Bin 940272 -> 940528 bytes
.../resources/avatar/avatar-animation.json | 10 +++++-----
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/interface/resources/avatar/animations/jog_fwd.fbx b/interface/resources/avatar/animations/jog_fwd.fbx
index 3c3b8118ab9ecf2988df13b7534ddeac181d72ec..f389fea36414926df660370b1ed73206bc752ad7 100644
GIT binary patch
literal 928272
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