TrackedObject00 | number | {@link Pose} | Deprecated:
diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h
index 7c3c31cb38..95d0524a4a 100644
--- a/libraries/controllers/src/controllers/InputDevice.h
+++ b/libraries/controllers/src/controllers/InputDevice.h
@@ -54,10 +54,9 @@ enum Hand {
/**jsdoc
* The Controller.Hardware object has properties representing standard and hardware-specific controller and
- * computer outputs, plus predefined actions on Interface and the user's avatar. Read-only. The outputs can be mapped
- * to actions or functions in a {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped
- * to standard controller outputs.
- *
+ * computer outputs, plus predefined actions on Interface and the user's avatar. Read-only.
+ * The outputs can be mapped to actions or functions in a {@link RouteObject} mapping. Additionally, hardware-specific
+ * controller outputs can be mapped to standard controller outputs.
* Controllers typically implement a subset of the {@link Controller.Standard} controls, plus they may implement some extras.
* Some common controllers are included in the table. You can see the outputs provided by these and others by
* viewing their {@link Controller.MappingJSON|MappingJSON} files at
diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp
index 07c59e1aaa..fd32b2eb43 100644
--- a/libraries/controllers/src/controllers/ScriptingInterface.cpp
+++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp
@@ -227,6 +227,7 @@ namespace controller {
}
QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
+ // FIXME: Implement. https://highfidelity.manuscript.com/f/cases/14188/Implement-Controller-loadMappping
return nullptr;
}
diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h
index 156fc1af7c..84396fc8be 100644
--- a/libraries/controllers/src/controllers/ScriptingInterface.h
+++ b/libraries/controllers/src/controllers/ScriptingInterface.h
@@ -73,7 +73,7 @@ namespace controller {
virtual ~ScriptingInterface() {};
/**jsdoc
- * Get a list of all available actions.
+ * Gets a list of all available actions.
* @function Controller.getAllActions
* @returns {Action[]} All available actions.
* @deprecated This function is deprecated and will be removed. It no longer works.
@@ -82,7 +82,7 @@ namespace controller {
Q_INVOKABLE QVector getAllActions();
/**jsdoc
- * Get a list of all available inputs for a hardware device.
+ * Gets a list of all available inputs for a hardware device.
* @function Controller.getAvailableInputs
* @param {number} deviceID - Integer ID of the hardware device.
* @returns {NamedPair[]} All available inputs for the device.
@@ -92,7 +92,7 @@ namespace controller {
Q_INVOKABLE QVector getAvailableInputs(unsigned int device);
/**jsdoc
- * Find the name of a particular controller from its device ID.
+ * Finds the name of a particular controller from its device ID.
* @function Controller.getDeviceName
* @param {number} deviceID - The integer ID of the device.
* @returns {string} The name of the device if found, otherwise "unknown" .
@@ -106,7 +106,7 @@ namespace controller {
Q_INVOKABLE QString getDeviceName(unsigned int device);
/**jsdoc
- * Get the current value of an action.
+ * Gets the current value of an action.
* @function Controller.getActionValue
* @param {number} actionID - The integer ID of the action.
* @returns {number} The current value of the action.
@@ -121,7 +121,7 @@ namespace controller {
Q_INVOKABLE float getActionValue(int action);
/**jsdoc
- * Find the ID of a specific controller from its device name.
+ * Finds the ID of a specific controller from its device name.
* @function Controller.findDevice
* @param {string} deviceName - The name of the device to find.
* @returns {number} The integer ID of the device if available, otherwise 65535 .
@@ -132,7 +132,7 @@ namespace controller {
Q_INVOKABLE int findDevice(QString name);
/**jsdoc
- * Get the names of all currently available controller devices plus "Actions", "Application", and "Standard".
+ * Gets the names of all currently available controller devices plus "Actions", "Application", and "Standard".
* @function Controller.getDeviceNames
* @returns {string[]} An array of device names.
* @example Get the names of all currently available controller devices.
@@ -143,7 +143,7 @@ namespace controller {
Q_INVOKABLE QVector getDeviceNames();
/**jsdoc
- * Find the ID of an action from its name.
+ * Finds the ID of an action from its name.
* @function Controller.findAction
* @param {string} actionName - The name of the action: one of the {@link Controller.Actions} property names.
* @returns {number} The integer ID of the action if found, otherwise 4095 . Note that this value is not
@@ -156,7 +156,7 @@ namespace controller {
Q_INVOKABLE int findAction(QString actionName);
/**jsdoc
- * Get the names of all actions available as properties of {@link Controller.Actions}.
+ * Gets the names of all actions available as properties of {@link Controller.Actions}.
* @function Controller.getActionNames
* @returns {string[]} An array of action names.
* @example Get the names of all actions.
@@ -167,7 +167,7 @@ namespace controller {
Q_INVOKABLE QVector getActionNames() const;
/**jsdoc
- * Get the value of a controller button or axis output. Note: Also gets the value of a controller axis output.
+ * Gets the value of a controller button or axis output. Note: Also gets the value of a controller axis output.
* @function Controller.getValue
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} item.
* @returns {number} The current value of the controller item output if source is valid, otherwise
@@ -186,7 +186,7 @@ namespace controller {
Q_INVOKABLE float getValue(const int& source) const;
/**jsdoc
- * Get the value of a controller axis output. Note: Also gets the value of a controller button output.
+ * Gets the value of a controller axis output. Note: Also gets the value of a controller button output.
* @function Controller.getAxisValue
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} item.
* @returns {number} The current value of the controller item output if source is valid, otherwise
@@ -196,7 +196,7 @@ namespace controller {
Q_INVOKABLE float getAxisValue(int source) const;
/**jsdoc
- * Get the value of a controller pose output.
+ * Gets the value of a controller pose output.
* @function Controller.getPoseValue
* @param {number} source - The {@link Controller.Standard} or {@link Controller.Hardware} pose output.
* @returns {Pose} The current value of the controller pose output if source is a pose output, otherwise
@@ -210,9 +210,9 @@ namespace controller {
/**jsdoc
* Triggers a haptic pulse on connected and enabled devices that have the capability.
* @function Controller.triggerHapticPulse
- * @param {number} strength - The strength of the haptic pulse, 0.0 – 1.0 .
+ * @param {number} strength - The strength of the haptic pulse, range 0.0 – 1.0 .
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
- * @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
+ * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
* @example Trigger a haptic pulse on the right hand.
* var HAPTIC_STRENGTH = 0.5;
* var HAPTIC_DURATION = 10;
@@ -224,8 +224,8 @@ namespace controller {
/**jsdoc
* Triggers a 250ms haptic pulse on connected and enabled devices that have the capability.
* @function Controller.triggerShortHapticPulse
- * @param {number} strength - The strength of the haptic pulse, 0.0 – 1.0 .
- * @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
+ * @param {number} strength - The strength of the haptic pulse, range 0.0 – 1.0 .
+ * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
*/
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
@@ -233,9 +233,9 @@ namespace controller {
* Triggers a haptic pulse on a particular device if connected and enabled and it has the capability.
* @function Controller.triggerHapticPulseOnDevice
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
- * @param {number} strength - The strength of the haptic pulse, 0.0 – 1.0 .
+ * @param {number} strength - The strength of the haptic pulse, range 0.0 – 1.0 .
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
- * @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
+ * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
* @example Trigger a haptic pulse on an Oculus Touch controller.
* var HAPTIC_STRENGTH = 0.5;
* var deviceID = Controller.findDevice("OculusTouch");
@@ -250,19 +250,19 @@ namespace controller {
* Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability.
* @function Controller.triggerShortHapticPulseOnDevice
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
- * @param {number} strength - The strength of the haptic pulse, 0.0 – 1.0 .
- * @param {Controller.Hand} hand=2 - The hand or hands to trigger the haptic pulse on.
+ * @param {number} strength - The strength of the haptic pulse, range 0.0 – 1.0 .
+ * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on.
*/
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH)
const;
/**jsdoc
- * Create a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
+ * Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
* routed to Standard controls, Actions , or script functions using {@link RouteObject}
* methods. The mapping can then be enabled using {@link Controller.enableMapping|enableMapping} for it to take effect.
* @function Controller.newMapping
- * @param {string} mappingName=Uuid.generate() - A unique name for the mapping. If not specified a new UUID generated
+ * @param {string} [mappingName=Uuid.generate()] - A unique name for the mapping. If not specified a new UUID generated
* by {@link Uuid.generate} is used.
* @returns {MappingObject} A controller mapping object.
* @example Create a simple mapping that makes the right trigger move your avatar up.
@@ -279,22 +279,22 @@ namespace controller {
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
/**jsdoc
- * Enable or disable a controller mapping. When enabled, the routes in the mapping have effect.
+ * Enables or disables a controller mapping. When enabled, the routes in the mapping have effect.
* @function Controller.enableMapping
* @param {string} mappingName - The name of the mapping.
- * @param {boolean} enable=true - If true then the mapping is enabled, otherwise it is disabled.
+ * @param {boolean} [[enable=true] - If true then the mapping is enabled, otherwise it is disabled.
*/
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
/**jsdoc
- * Disable a controller mapping. When disabled, the routes in the mapping have no effect.
+ * Disables a controller mapping. When disabled, the routes in the mapping have no effect.
* @function Controller.disableMapping
* @param {string} mappingName - The name of the mapping.
*/
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
/**jsdoc
- * Create a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} string. Use
+ * Creates a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} string. Use
* {@link Controller.enableMapping|enableMapping} to enable the mapping for it to take effect.
* @function Controller.parseMapping
* @param {string} jsonString - A JSON string of the {@link Controller.MappingJSON|MappingJSON}.
@@ -317,19 +317,19 @@ namespace controller {
Q_INVOKABLE QObject* parseMapping(const QString& json);
/**jsdoc
- * Create a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} JSON file at a URL. Use
+ * Creates a new controller mapping from a {@link Controller.MappingJSON|MappingJSON} JSON file at a URL. Use
* {@link Controller.enableMapping|enableMapping} to enable the mapping for it to take effect.
+ * Warning: This function is not yet implemented; it doesn't load a mapping and just returns
+ * null .
* @function Controller.loadMapping
* @param {string} jsonURL - The URL the {@link Controller.MappingJSON|MappingJSON} JSON file.
* @returns {MappingObject} A controller mapping object.
- * @todo Implement this function. It currently does not load the mapping from the file; it just returns
- * null .
*/
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
/**jsdoc
- * Get the {@link Controller.Hardware} property tree. Calling this function is the same as using the {@link Controller}
+ * Gets the {@link Controller.Hardware} property tree. Calling this function is the same as using the {@link Controller}
* property, Controller.Hardware .
* @function Controller.getHardware
* @returns {Controller.Hardware} The {@link Controller.Hardware} property tree.
@@ -337,7 +337,7 @@ namespace controller {
Q_INVOKABLE const QVariantMap getHardware() { return _hardware; }
/**jsdoc
- * Get the {@link Controller.Actions} property tree. Calling this function is the same as using the {@link Controller}
+ * Gets the {@link Controller.Actions} property tree. Calling this function is the same as using the {@link Controller}
* property, Controller.Actions .
* @function Controller.getActions
* @returns {Controller.Actions} The {@link Controller.Actions} property tree.
@@ -345,7 +345,7 @@ namespace controller {
Q_INVOKABLE const QVariantMap getActions() { return _actions; } //undefined
/**jsdoc
- * Get the {@link Controller.Standard} property tree. Calling this function is the same as using the {@link Controller}
+ * Gets the {@link Controller.Standard} property tree. Calling this function is the same as using the {@link Controller}
* property, Controller.Standard .
* @function Controller.getStandard
* @returns {Controller.Standard} The {@link Controller.Standard} property tree.
@@ -354,7 +354,7 @@ namespace controller {
/**jsdoc
- * Start making a recording of currently active controllers.
+ * Starts making a recording of currently active controllers.
* @function Controller.startInputRecording
* @example Make a controller recording.
* // Delay start of recording for 2s.
@@ -374,13 +374,13 @@ namespace controller {
Q_INVOKABLE void startInputRecording();
/**jsdoc
- * Stop making a recording started by {@link Controller.startInputRecording|startInputRecording}.
+ * Stops making a recording started by {@link Controller.startInputRecording|startInputRecording}.
* @function Controller.stopInputRecording
*/
Q_INVOKABLE void stopInputRecording();
/**jsdoc
- * Play back the current recording from the beginning. The current recording may have been recorded by
+ * Plays back the current recording from the beginning. The current recording may have been recorded by
* {@link Controller.startInputRecording|startInputRecording} and
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
* {@link Controller.loadInputRecording|loadInputRecording}. Playback repeats in a loop until
@@ -403,13 +403,13 @@ namespace controller {
Q_INVOKABLE void startInputPlayback();
/**jsdoc
- * Stop play back of a recording started by {@link Controller.startInputPlayback|startInputPlayback}.
+ * Stops play back of a recording started by {@link Controller.startInputPlayback|startInputPlayback}.
* @function Controller.stopInputPlayback
*/
Q_INVOKABLE void stopInputPlayback();
/**jsdoc
- * Save the current recording to a file. The current recording may have been recorded by
+ * Saves the current recording to a file. The current recording may have been recorded by
* {@link Controller.startInputRecording|startInputRecording} and
* {@link Controller.stopInputRecording|stopInputRecording}, or loaded by
* {@link Controller.loadInputRecording|loadInputRecording}. It is saved in the directory returned by
@@ -419,24 +419,26 @@ namespace controller {
Q_INVOKABLE void saveInputRecording();
/**jsdoc
- * Load an input recording, ready for play back.
+ * Loads an input recording, ready for play back.
* @function Controller.loadInputRecording
* @param {string} file - The path to the recording file, prefixed by "file:///" .
*/
Q_INVOKABLE void loadInputRecording(const QString& file);
/**jsdoc
- * Get the directory in which input recordings are saved.
+ * Gets the directory in which input recordings are saved.
* @function Controller.getInputRecorderSaveDirectory
* @returns {string} The directory in which input recordings are saved.
*/
Q_INVOKABLE QString getInputRecorderSaveDirectory();
/**jsdoc
- * Get all the active and enabled (running) input devices
- * @function Controller.getRunningInputDevices
- * @returns {string[]} An array of strings with the names
- */
+ * Gets the names of all the active and running (enabled) input devices.
+ * @function Controller.getRunningInputDevices
+ * @returns {string[]} The list of current active and running input devices.
+ * @example List all active and running input devices.
+ * print("Running devices: " + JSON.stringify(Controller.getRunningInputDeviceNames()));
+ */
Q_INVOKABLE QStringList getRunningInputDeviceNames();
bool isMouseCaptured() const { return _mouseCaptured; }
@@ -447,7 +449,7 @@ namespace controller {
public slots:
/**jsdoc
- * Disable processing of mouse "move", "press", "double-press", and "release" events into
+ * Disables processing of mouse "move", "press", "double-press", and "release" events into
* {@link Controller.Hardware|Controller.Hardware.Keyboard} outputs.
* @function Controller.captureMouseEvents
* @example Disable Controller.Hardware.Keyboard mouse events for a short period.
@@ -475,7 +477,7 @@ namespace controller {
virtual void captureMouseEvents() { _mouseCaptured = true; }
/**jsdoc
- * Enable processing of mouse "move", "press", "double-press", and "release" events into
+ * Enables processing of mouse "move", "press", "double-press", and "release" events into
* {@link Controller.Hardware-Keyboard|Controller.Hardware.Keyboard} outputs that were disabled using
* {@link Controller.captureMouseEvents|captureMouseEvents}.
* @function Controller.releaseMouseEvents
@@ -484,7 +486,7 @@ namespace controller {
/**jsdoc
- * Disable processing of touch "begin", "update", and "end" events into
+ * Disables processing of touch "begin", "update", and "end" events into
* {@link Controller.Hardware|Controller.Hardware.Keyboard},
* {@link Controller.Hardware|Controller.Hardware.Touchscreen}, and
* {@link Controller.Hardware|Controller.Hardware.TouchscreenVirtualPad} outputs.
@@ -493,7 +495,7 @@ namespace controller {
virtual void captureTouchEvents() { _touchCaptured = true; }
/**jsdoc
- * Enable processing of touch "begin", "update", and "end" events into
+ * Enables processing of touch "begin", "update", and "end" events into
* {@link Controller.Hardware|Controller.Hardware.Keyboard},
* {@link Controller.Hardware|Controller.Hardware.Touchscreen}, and
* {@link Controller.Hardware|Controller.Hardware.TouchscreenVirtualPad} outputs that were disabled using
@@ -504,14 +506,14 @@ namespace controller {
/**jsdoc
- * Disable processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
+ * Disables processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
* outputs.
* @function Controller.captureWheelEvents
*/
virtual void captureWheelEvents() { _wheelCaptured = true; }
/**jsdoc
- * Enable processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
+ * Enables processing of mouse wheel rotation events into {@link Controller.Hardware|Controller.Hardware.Keyboard}
* outputs that wer disabled using {@link Controller.captureWheelEvents|captureWheelEvents}.
* @function Controller.releaseWheelEvents
*/
@@ -519,7 +521,7 @@ namespace controller {
/**jsdoc
- * Disable translating and rotating the user's avatar in response to keyboard and controller controls.
+ * Disables translating and rotating the user's avatar in response to keyboard and controller controls.
* @function Controller.captureActionEvents
* @example Disable avatar translation and rotation for a short period.
* Script.setTimeout(function () {
@@ -533,12 +535,19 @@ namespace controller {
virtual void captureActionEvents() { _actionsCaptured = true; }
/**jsdoc
- * Enable translating and rotating the user's avatar in response to keyboard and controller controls that were disabled
+ * Enables translating and rotating the user's avatar in response to keyboard and controller controls that were disabled
* using {@link Controller.captureActionEvents|captureActionEvents}.
* @function Controller.releaseActionEvents
*/
virtual void releaseActionEvents() { _actionsCaptured = false; }
+ /**jsdoc
+ * @function Controller.updateRunningInputDevices
+ * @param {string} deviceName - Device name.
+ * @param {boolean} isRunning - Is running.
+ * @param {string[]} runningDevices - Running devices.
+ * @deprecated This function is deprecated and will be removed.
+ */
void updateRunningInputDevices(const QString& deviceName, bool isRunning, const QStringList& runningDevices);
signals:
@@ -593,7 +602,7 @@ namespace controller {
/**jsdoc
* Triggered when a device is registered or unregistered by a plugin. Not all plugins generate
- * hardwareChanged events: for example connecting or disconnecting a mouse will not generate an event but
+ * hardwareChanged events: for example, connecting or disconnecting a mouse will not generate an event but
* connecting or disconnecting an Xbox controller will.
* @function Controller.hardwareChanged
* @returns {Signal}
@@ -601,13 +610,13 @@ namespace controller {
void hardwareChanged();
/**jsdoc
- * Triggered when a device is enabled/disabled
- * Enabling/Disabling Leapmotion on settings/controls will trigger this signal.
- * @function Controller.deviceRunningChanged
- * @param {string} deviceName - The name of the device that is getting enabled/disabled
- * @param {boolean} isEnabled - Return if the device is enabled.
- * @returns {Signal}
- */
+ * Triggered when an input device starts or stops being active and running (enabled). For example, enabling or
+ * disabling the LeapMotion in Settings > Controls > Calibration will trigger this signal.
+ * @function Controller.inputDeviceRunningChanged
+ * @param {string} deviceName - The name of the device.
+ * @param {boolean} isRunning - true if the device is active and running, false if it isn't.
+ * @returns {Signal}
+ */
void inputDeviceRunningChanged(QString deviceName, bool isRunning);
diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp
index e1733d2524..ece10ecca3 100644
--- a/libraries/controllers/src/controllers/StandardController.cpp
+++ b/libraries/controllers/src/controllers/StandardController.cpp
@@ -30,17 +30,16 @@ void StandardController::focusOutEvent() {
/**jsdoc
* The Controller.Standard object has properties representing standard controller outputs. Those for physical
* controllers are based on the XBox controller, with aliases for PlayStation. The property values are integer IDs, uniquely
- * identifying each output. Read-only. These can be mapped to actions or functions in a {@link RouteObject}
- * mapping.
- *
- * The data value provided by each control is either a number or a {@link Pose}. Numbers are typically normalized to
- * 0.0 or 1.0 for button states, the range 0.0 – 1.0 for unidirectional scales,
- * and the range -1.0 – 1.0 for bidirectional scales.
- *
- * Each hardware device has a mapping from its outputs to Controller.Standard items, specified in a JSON file.
- * For example,
- * leapmotion.json and
- * vive.json.
+ * identifying each output. Read-only.
+ * These outputs can be mapped to actions or functions in a {@link RouteObject} mapping. The data value provided by each
+ * control is either a number or a {@link Pose}. Numbers are typically normalized to 0.0 or 1.0 for
+ * button states, the range 0.0 – 1.0 for unidirectional scales, and the range
+ * -1.0 – 1.0 for bidirectional scales.
+ * Each hardware device has a mapping from its outputs to a subset of Controller.Standard items, specified in a
+ * JSON file. For example,
+ * vive.json
+ * and
+ * leapmotion.json.
*
*
*
@@ -119,12 +118,12 @@ void StandardController::focusOutEvent() {
* button.
* RightThumbUp | number | number | Right thumb not touching primary or secondary
* thumb buttons. |
- * LeftPrimaryIndex | number | number | Left primary index control pressed.
- * To Do: Implement this for current controllers. |
+ * LeftPrimaryIndex | number | number | Left primary index control
+ * pressed. |
* LeftSecondaryIndex | number | number | Left secondary index control pressed.
* |
* RightPrimaryIndex | number | number | Right primary index control pressed.
- * To Do: Implement this for current controllers. |
+ *
* RightSecondaryIndex | number | number | Right secondary index control pressed.
* |
* LeftPrimaryIndexTouch | number | number | Left index finger is touching primary
diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
index 845e19f6c3..f0a823a3de 100644
--- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
@@ -84,12 +84,12 @@ class UserInputMapper;
/**jsdoc
* A route in a {@link Controller.MappingJSON}.
* @typedef {object} Controller.MappingJSONRoute
- * @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property name or an axis
- * made from them. If a property name, the leading "Controller.Hardware." can be omitted.
- * @property {boolean} [peek=false] - If true then peeking is enabled per {@link RouteObject#peek}.
- * @property {boolean} [debug=false] - If true then debug is enabled per {@link RouteObject#debug}.
+ * @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property or an axis made from
+ * them. If a property name, the leading "Controller.Hardware." can be omitted.
+ * @property {boolean} [peek=false] - If true, then peeking is enabled per {@link RouteObject#peek}.
+ * @property {boolean} [debug=false] - If true , then debug is enabled per {@link RouteObject#debug}.
* @property {string|string[]} [when=[]] - One or more numeric {@link Controller.Hardware} property names which are evaluated
- * as booleans and ANDed together. Prepend with a ! to use the logical NOT of the property value. The leading
+ * as booleans and ANDed together. Prepend a property name with a ! to do a logical NOT. The leading
* "Controller.Hardware." can be omitted from the property names.
* @property {Controller.MappingJSONFilter|Controller.MappingJSONFilter[]} [filters=[]] - One or more filters in the route.
* @property {string} to - The name of a {@link Controller.Actions} or {@link Controller.Standard} property. The leading
@@ -134,7 +134,7 @@ public:
: _parent(parent), _mapping(mapping) { }
/**jsdoc
- * Create a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
+ * Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
* function.
* This is a QML-specific version of {@link MappingObject#from|from}: use this version in QML files.
* @function MappingObject#fromQml
@@ -145,7 +145,7 @@ public:
Q_INVOKABLE QObject* fromQml(const QJSValue& source);
/**jsdoc
- * Create a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
+ * Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.
* This is a QML-specific version of {@link MappingObject#makeAxis|makeAxis}: use this version in QML files.
* @function MappingObject#makeAxisQml
@@ -157,7 +157,7 @@ public:
Q_INVOKABLE QObject* makeAxisQml(const QJSValue& source1, const QJSValue& source2);
/**jsdoc
- * Create a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
+ * Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
* function.
* @function MappingObject#from
* @param {Controller.Standard|Controller.Hardware|function} source - The controller output or function that is the source
@@ -167,7 +167,7 @@ public:
Q_INVOKABLE QObject* from(const QScriptValue& source);
/**jsdoc
- * Create a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
+ * Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.
* @function MappingObject#makeAxis
* @param {Controller.Hardware} source1 - The first, negative-direction controller output.
@@ -189,7 +189,7 @@ public:
Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2);
/**jsdoc
- * Enable or disable the mapping. When enabled, the routes in the mapping take effect.
+ * Enables or disables the mapping. When enabled, the routes in the mapping take effect.
* Synonymous with {@link Controller.enableMapping}.
* @function MappingObject#enable
* @param {boolean} enable=true - If true then the mapping is enabled, otherwise it is disabled.
@@ -198,7 +198,7 @@ public:
Q_INVOKABLE QObject* enable(bool enable = true);
/**jsdoc
- * Disable the mapping. When disabled, the routes in the mapping have no effect.
+ * Disables the mapping. When disabled, the routes in the mapping have no effect.
* Synonymous with {@link Controller.disableMapping}.
* @function MappingObject#disable
* @returns {MappingObject} The mapping object, so that further routes can be added.
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp
index 048e23be1c..56ace23335 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp
@@ -66,6 +66,8 @@ QObject* RouteBuilderProxy::peek(bool enable) {
}
QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
+ // FIXME: Support "!" conditional in simple expression and array expression.
+ // Note that "!" is supported when parsing a JSON file, in UserInputMapper::parseConditional().
auto newConditional = _parent.conditionalFor(expression);
if (_route->conditional) {
_route->conditional = ConditionalPointer(new AndConditional(_route->conditional, newConditional));
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
index eb610af78a..e7ff04d72c 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
@@ -51,7 +51,7 @@ class RouteBuilderProxy : public QObject {
: _parent(parent), _mapping(mapping), _route(route) { }
/**jsdoc
- * Terminate the route with a standard control, an action, or a script function. The output value from the route is
+ * Terminates the route with a standard control, an action, or a script function. The output value from the route is
* sent to the specified destination.
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
* @function RouteObject#toQml
@@ -62,7 +62,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE void toQml(const QJSValue& destination);
/**jsdoc
- * Process the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
+ * Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
* the input is read only if the condition is true . Thus, if the condition is not met then subsequent
* routes using the same input are processed.
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
@@ -81,7 +81,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* whenQml(const QJSValue& expression);
/**jsdoc
- * Terminate the route with a standard control, an action, or a script function. The output value from the route is
+ * Terminates the route with a standard control, an action, or a script function. The output value from the route is
* sent to the specified destination.
* @function RouteObject#to
* @param {Controller.Standard|Controller.Actions|function} destination - The standard control, action, or JavaScript
@@ -117,7 +117,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE void to(const QScriptValue& destination);
/**jsdoc
- * Enable and disabling writing debug information for a route to the program log.
+ * Enables or disables writing debug information for a route to the program log.
* @function RouteObject#debug
* @param {boolean} [enable=true] - If true then writing debug information is enabled for the route,
* otherwise it is disabled.
@@ -147,7 +147,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* debug(bool enable = true);
/**jsdoc
- * Process the route without marking the controller output as having been read, so that other routes from the same
+ * Processes the route without marking the controller output as having been read, so that other routes from the same
* controller output can also process.
* @function RouteObject#peek
* @param {boolean} [enable=true] - If true then the route is processed without marking the route's
@@ -157,7 +157,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* peek(bool enable = true);
/**jsdoc
- * Process the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
+ * Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
* the input is read only if the condition is true . Thus, if the condition is not met then subsequent
* routes using the same input are processed.
* @function RouteObject#when
@@ -170,6 +170,8 @@ class RouteBuilderProxy : public QObject {
* definition.
*
* If an array of conditions is provided, their values are ANDed together.
+ * Warning: The use of ! is not currently supported in JavaScript .when()
+ * calls.
* @returns {RouteObject} The RouteObject with the condition added.
* @example Process the right trigger differently in HMD and desktop modes.
* var MAPPING_NAME = "com.highfidelity.controllers.example.newMapping";
@@ -193,7 +195,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* when(const QScriptValue& expression);
/**jsdoc
- * Filter numeric route values to lie between two values; values outside this range are not passed on through the
+ * Filters numeric route values to lie between two values; values outside this range are not passed on through the
* route.
* @function RouteObject#clamp
* @param {number} min - The minimum value to pass through.
@@ -214,7 +216,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* clamp(float min, float max);
/**jsdoc
- * Filter numeric route values such that they are rounded to 0 or 1 without output values
+ * Filters numeric route values such that they are rounded to 0 or 1 without output values
* flickering when the input value hovers around 0.5 . For example, this enables you to use an analog input
* as if it were a toggle.
* @function RouteObject#hysteresis
@@ -239,7 +241,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* hysteresis(float min, float max);
/**jsdoc
- * Filter numeric route values to send at a specified interval.
+ * Filters numeric route values to send at a specified interval.
* @function RouteObject#pulse
* @param {number} interval - The interval between sending values, in seconds.
* @returns {RouteObject} The RouteObject with the filter applied.
@@ -258,7 +260,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* pulse(float interval);
/**jsdoc
- * Filter numeric and {@link Pose} route values to be scaled by a constant amount.
+ * Filters numeric and {@link Pose} route values to be scaled by a constant amount.
* @function RouteObject#scale
* @param {number} multiplier - The scale to multiply the value by.
* @returns {RouteObject} The RouteObject with the filter applied.
@@ -280,7 +282,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* scale(float multiplier);
/**jsdoc
- * Filter numeric and {@link Pose} route values to have the opposite sign, e.g., 0.5 is changed to
+ * Filters numeric and {@link Pose} route values to have the opposite sign, e.g., 0.5 is changed to
* -0.5 .
* @function RouteObject#invert
* @returns {RouteObject} The RouteObject with the filter applied.
@@ -302,7 +304,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* invert();
/**jsdoc
- * Filter numeric route values such that they're sent only when the input value is outside a dead-zone. When the input
+ * Filters numeric route values such that they're sent only when the input value is outside a dead-zone. When the input
* passes the dead-zone value, output is sent starting at 0.0 and catching up with the input value. As the
* input returns toward the dead-zone value, output values reduce to 0.0 at the dead-zone value.
* @function RouteObject#deadZone
@@ -324,7 +326,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* deadZone(float min);
/**jsdoc
- * Filter numeric route values such that they are rounded to -1 , 0 , or 1 .
+ * Filters numeric route values such that they are rounded to -1 , 0 , or 1 .
* For example, this enables you to use an analog input as if it were a toggle or, in the case of a bidirectional axis,
* a tri-state switch.
* @function RouteObject#constrainToInteger
@@ -345,7 +347,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* constrainToInteger();
/**jsdoc
- * Filter numeric route values such that they are rounded to 0 or 1 . For example, this
+ * Filters numeric route values such that they are rounded to 0 or 1 . For example, this
* enables you to use an analog input as if it were a toggle.
* @function RouteObject#constrainToPositiveInteger
* @returns {RouteObject} The RouteObject with the filter applied.
@@ -364,7 +366,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* constrainToPositiveInteger();
/**jsdoc
- * Filter {@link Pose} route values to have a pre-translation applied.
+ * Filters {@link Pose} route values to have a pre-translation applied.
* @function RouteObject#translate
* @param {Vec3} translate - The pre-translation to add to the pose.
* @returns {RouteObject} The RouteObject with the pre-translation applied.
@@ -373,7 +375,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* translate(glm::vec3 translate);
/**jsdoc
- * Filter {@link Pose} route values to have a pre-transform applied.
+ * Filters {@link Pose} route values to have a pre-transform applied.
* @function RouteObject#transform
* @param {Mat4} transform - The pre-transform to apply.
* @returns {RouteObject} The RouteObject with the pre-transform applied.
@@ -382,7 +384,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* transform(glm::mat4 transform);
/**jsdoc
- * Filter {@link Pose} route values to have a post-transform applied.
+ * Filters {@link Pose} route values to have a post-transform applied.
* @function RouteObject#postTransform
* @param {Mat4} transform - The post-transform to apply.
* @returns {RouteObject} The RouteObject with the post-transform applied.
@@ -391,7 +393,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
/**jsdoc
- * Filter {@link Pose} route values to have a pre-rotation applied.
+ * Filters {@link Pose} route values to have a pre-rotation applied.
* @function RouteObject#rotate
* @param {Quat} rotation - The pre-rotation to add to the pose.
* @returns {RouteObject} The RouteObject with the pre-rotation applied.
@@ -400,7 +402,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* rotate(glm::quat rotation);
/**jsdoc
- * Filter {@link Pose} route values to be smoothed by a low velocity filter. The filter's rotation and translation
+ * Filters {@link Pose} route values to be smoothed by a low velocity filter. The filter's rotation and translation
* values are calculated as: (1 - f) * currentValue + f * previousValue where
* f = currentVelocity / filterConstant . At low velocities, the filter value is largely the previous
* value; at high velocities the value is wholly the current controller value.
@@ -415,7 +417,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
/**jsdoc
- * Filter {@link Pose} route values to be smoothed by an exponential decay filter. The filter's rotation and
+ * Filters {@link Pose} route values to be smoothed by an exponential decay filter. The filter's rotation and
* translation values are calculated as: filterConstant * currentValue + (1 - filterConstant) *
* previousValue . Values near 1 are less smooth with lower latency; values near 0 are more smooth with higher
* latency.
@@ -428,7 +430,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* exponentialSmoothing(float rotationConstant, float translationConstant);
/**jsdoc
- * Filter numeric route values such that a value of 0.0 is changed to 1.0 , and other values
+ * Filters numeric route values such that a value of 0.0 is changed to 1.0 , and other values
* are changed to 0.0 .
* @function RouteObject#logicalNot
* @returns {RouteObject} The RouteObject with the filter applied.
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index 0dd8b3f268..bb94195f85 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -38,6 +38,7 @@
#include
#include
+#include
#include
#include
#include
@@ -681,11 +682,14 @@ void OpenGLDisplayPlugin::compositeLayers() {
compositeExtra();
}
- // Draw the pointer last so it's on top of everything
- auto compositorHelper = DependencyManager::get();
- if (compositorHelper->getReticleVisible()) {
- PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
+ auto& cursorManager = Cursor::Manager::instance();
+ if (isHmd() || cursorManager.getCursor()->getIcon() == Cursor::RETICLE) {
+ auto compositorHelper = DependencyManager::get();
+ // Draw the pointer last so it's on top of everything
+ if (compositorHelper->getReticleVisible()) {
+ PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
compositePointer();
+ }
}
}
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 6cfff7bc41..e8ce812285 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -205,8 +205,11 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
foreach (const EntityItemID& entityID, entitiesWithEntityScripts) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
- if (entityItem) {
+ if (entityItem && !entityItem->getScript().isEmpty()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
+ if (entityItem->contains(_avatarPosition)) {
+ _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
+ }
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
}
@@ -217,6 +220,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
stopDomainAndNonOwnedEntities();
+ auto sessionUUID = getTree()->getMyAvatarSessionUUID();
std::unordered_map savedEntities;
// remove all entities from the scene
auto scene = _viewState->getMain3DScene();
@@ -224,7 +228,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
const EntityItemPointer& entityItem = renderer->getEntity();
- if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
+ if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == sessionUUID))) {
fadeOutRenderable(renderer);
} else {
savedEntities[entry.first] = entry.second;
@@ -235,7 +239,9 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
_renderablesToUpdate = savedEntities;
_entitiesInScene = savedEntities;
- _layeredZones.clearNonLocalLayeredZones();
+ if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) {
+ applyLayeredZones();
+ }
OctreeProcessor::clearDomainAndNonOwnedEntities();
}
@@ -268,6 +274,9 @@ void EntityTreeRenderer::clear() {
// reset the zone to the default (while we load the next scene)
_layeredZones.clear();
+ if (!_shuttingDown) {
+ applyLayeredZones();
+ }
OctreeProcessor::clear();
}
@@ -360,6 +369,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
for (const auto& processedId : processedIds) {
_entitiesToAdd.erase(processedId);
}
+ forceRecheckEntities();
}
}
}
@@ -534,8 +544,7 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair proxyUp
_spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second);
}
-bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar) {
- bool didUpdate = false;
+void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar) {
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
QVector entityIDs;
@@ -547,7 +556,7 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorevalEntitiesInSphere(_avatarPosition, radius, PickFilter(), entityIDs);
- LayeredZones oldLayeredZones(std::move(_layeredZones));
+ LayeredZones oldLayeredZones(_layeredZones);
_layeredZones.clear();
// create a list of entities that actually contain the avatar's position
@@ -575,36 +584,26 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorgetVisible() && renderableForEntity(entity)) {
- _layeredZones.insert(std::dynamic_pointer_cast(entity));
+ if (isZone && entity->getVisible() && renderableIdForEntity(entity) != render::Item::INVALID_ITEM_ID) {
+ _layeredZones.emplace(std::dynamic_pointer_cast(entity));
}
if ((!hasScript && isZone) || scriptHasLoaded) {
- if (entitiesContainingAvatar) {
- *entitiesContainingAvatar << entity->getEntityItemID();
- }
+ entitiesContainingAvatar << entity->getEntityItemID();
}
}
}
- // check if our layered zones have changed
- if ((_layeredZones.empty() && oldLayeredZones.empty()) || (!oldLayeredZones.empty() && _layeredZones.contains(oldLayeredZones))) {
- return;
+ if (!_layeredZones.equals(oldLayeredZones)) {
+ applyLayeredZones();
}
-
- applyLayeredZones();
-
- didUpdate = true;
});
-
- return didUpdate;
}
-bool EntityTreeRenderer::checkEnterLeaveEntities() {
+void EntityTreeRenderer::checkEnterLeaveEntities() {
PROFILE_RANGE(simulation_physics, "EnterLeave");
PerformanceTimer perfTimer("enterLeave");
auto now = usecTimestampNow();
- bool didUpdate = false;
if (_tree && !_shuttingDown) {
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
@@ -616,44 +615,43 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
auto movedEnough = glm::distance(avatarPosition, _avatarPosition) > ZONE_CHECK_DISTANCE;
auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL;
- if (movedEnough || enoughTimeElapsed) {
+ if (_forceRecheckEntities || movedEnough || enoughTimeElapsed) {
_avatarPosition = avatarPosition;
_lastZoneCheck = now;
- QVector entitiesContainingAvatar;
- didUpdate = findBestZoneAndMaybeContainingEntities(&entitiesContainingAvatar);
-
+ _forceRecheckEntities = false;
+
+ QSet entitiesContainingAvatar;
+ findBestZoneAndMaybeContainingEntities(entitiesContainingAvatar);
+
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
// for entity IDs that no longer exist.
- // for all of our previous containing entities, if they are no longer containing then send them a leave event
- foreach(const EntityItemID& entityID, _currentEntitiesInside) {
- if (!entitiesContainingAvatar.contains(entityID)) {
- emit leaveEntity(entityID);
- if (_entitiesScriptEngine) {
+ if (_entitiesScriptEngine) {
+ // for all of our previous containing entities, if they are no longer containing then send them a leave event
+ foreach(const EntityItemID& entityID, _currentEntitiesInside) {
+ if (!entitiesContainingAvatar.contains(entityID)) {
+ emit leaveEntity(entityID);
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
}
- }
- // for all of our new containing entities, if they weren't previously containing then send them an enter event
- foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
- if (!_currentEntitiesInside.contains(entityID)) {
- emit enterEntity(entityID);
- if (_entitiesScriptEngine) {
+ // for all of our new containing entities, if they weren't previously containing then send them an enter event
+ foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
+ if (!_currentEntitiesInside.contains(entityID)) {
+ emit enterEntity(entityID);
_entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity");
}
}
+ _currentEntitiesInside = entitiesContainingAvatar;
}
- _currentEntitiesInside = entitiesContainingAvatar;
}
}
- return didUpdate;
}
void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
if (_tree && !_shuttingDown) {
- QVector currentEntitiesInsideToSave;
+ QSet currentEntitiesInsideToSave;
foreach (const EntityItemID& entityID, _currentEntitiesInside) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
@@ -662,7 +660,7 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
} else {
- currentEntitiesInsideToSave.push_back(entityID);
+ currentEntitiesInsideToSave.insert(entityID);
}
}
@@ -687,9 +685,7 @@ void EntityTreeRenderer::leaveAllEntities() {
}
void EntityTreeRenderer::forceRecheckEntities() {
- // make sure our "last avatar position" is something other than our current position,
- // so that on our next chance, we'll check for enter/leave entity events.
- _avatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
+ _forceRecheckEntities = true;
}
bool EntityTreeRenderer::applyLayeredZones() {
@@ -697,18 +693,12 @@ bool EntityTreeRenderer::applyLayeredZones() {
// in the expected layered order and update the scene with it
auto scene = _viewState->getMain3DScene();
if (scene) {
- render::Transaction transaction;
render::ItemIDs list;
- for (auto& zone : _layeredZones) {
- auto id = renderableIdForEntity(zone.zone);
- // The zone may not have been rendered yet.
- if (id != render::Item::INVALID_ITEM_ID) {
- list.push_back(id);
- }
- }
- render::Selection selection("RankedZones", list);
- transaction.resetSelection(selection);
+ _layeredZones.appendRenderIDs(list, this);
+ render::Selection selection("RankedZones", list);
+ render::Transaction transaction;
+ transaction.resetSelection(selection);
scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::applyLayeredZones(), Unexpected null scene, possibly during application shutdown";
@@ -992,7 +982,10 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
return;
}
- if (_tree && !_shuttingDown && _entitiesScriptEngine) {
+ if (_tree && !_shuttingDown && _entitiesScriptEngine && !itr->second->getEntity()->getScript().isEmpty()) {
+ if (itr->second->getEntity()->contains(_avatarPosition)) {
+ _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
+ }
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
@@ -1016,7 +1009,6 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
}
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
- forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities
checkAndCallPreload(entityID);
auto entity = std::static_pointer_cast(_tree)->findEntityByID(entityID);
if (entity) {
@@ -1038,6 +1030,9 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
QString scriptUrl = entity->getScript();
if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
if (_entitiesScriptEngine) {
+ if (entity->contains(_avatarPosition)) {
+ _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
+ }
_entitiesScriptEngine->unloadEntityScript(entityID);
}
entity->scriptHasUnloaded();
@@ -1185,107 +1180,98 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
}
void EntityTreeRenderer::updateZone(const EntityItemID& id) {
- // Get in the zone!
- auto zone = std::dynamic_pointer_cast(getTree()->findEntityByEntityItemID(id));
- if (zone && zone->contains(_avatarPosition)) {
- _layeredZones.update(zone);
+ if (auto zone = std::dynamic_pointer_cast(getTree()->findEntityByEntityItemID(id))) {
+ _layeredZones.update(zone, _avatarPosition, this);
+ applyLayeredZones();
}
}
-EntityTreeRenderer::LayeredZones::LayeredZones(LayeredZones&& other) {
- // In a swap:
- // > All iterators and references remain valid. The past-the-end iterator is invalidated.
- bool isSkyboxLayerValid = (other._skyboxLayer != other.end());
+bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& sessionUUID) {
+ bool zonesChanged = false;
- swap(other);
- _map.swap(other._map);
- _skyboxLayer = other._skyboxLayer;
+ auto it = c.begin();
+ while (it != c.end()) {
+ auto zone = it->zone.lock();
+ if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) {
+ zonesChanged = true;
+ it = c.erase(it);
+ } else {
+ it++;
+ }
+ }
- if (!isSkyboxLayerValid) {
- _skyboxLayer = end();
+ if (zonesChanged) {
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+ return zonesChanged;
+}
+
+std::pair EntityTreeRenderer::LayeredZones::getZoneInteractionProperties() const {
+ auto it = c.cbegin();
+ while (it != c.cend()) {
+ auto zone = it->zone.lock();
+ if (zone && zone->isDomainEntity()) {
+ return { zone->getFlyingAllowed(), zone->getGhostingAllowed() };
+ }
+ it++;
+ }
+ return { true, true };
+}
+
+void EntityTreeRenderer::LayeredZones::remove(const std::shared_ptr& zone) {
+ auto it = c.begin();
+ while (it != c.end()) {
+ if (it->zone.lock() == zone) {
+ break;
+ }
+ it++;
+ }
+ if (it != c.end()) {
+ c.erase(it);
+ std::make_heap(c.begin(), c.end(), comp);
}
}
-void EntityTreeRenderer::LayeredZones::clearNonLocalLayeredZones() {
- std::set localLayeredZones;
- std::map newMap;
+void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) {
+ // When a zone's position or visibility changes, we call this method
+ // In order to resort our zones, we first remove the changed zone, and then re-insert it if necessary
+ remove(zone);
- for (auto iter = begin(); iter != end(); iter++) {
- LayeredZone layeredZone = *iter;
+ // Only call contains if the zone is rendering
+ if (zone->isVisible() && entityTreeRenderer->renderableIdForEntity(zone) != render::Item::INVALID_ITEM_ID && zone->contains(position)) {
+ emplace(zone);
+ }
+}
- if (layeredZone.zone->isLocalEntity()) {
- bool success;
- iterator it;
- std::tie(it, success) = localLayeredZones.insert(layeredZone);
+bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const {
+ if (size() != other.size()) {
+ return false;
+ }
- if (success) {
- newMap.emplace(layeredZone.id, it);
+ auto it = c.cbegin();
+ auto otherIt = other.c.cbegin();
+ while (it != c.cend()) {
+ if (*it != *otherIt) {
+ return false;
+ }
+ it++;
+ otherIt++;
+ }
+
+ return true;
+}
+
+void EntityTreeRenderer::LayeredZones::appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const {
+ auto it = c.cbegin();
+ while (it != c.cend()) {
+ if (it->zone.lock()) {
+ auto id = entityTreeRenderer->renderableIdForEntityId(it->id);
+ if (id != render::Item::INVALID_ITEM_ID) {
+ list.push_back(id);
}
}
+ it++;
}
-
- std::set::operator=(localLayeredZones);
- _map = newMap;
- _skyboxLayer = empty() ? end() : begin();
-}
-
-void EntityTreeRenderer::LayeredZones::clear() {
- std::set::clear();
- _map.clear();
- _skyboxLayer = end();
-}
-
-std::pair EntityTreeRenderer::LayeredZones::insert(const LayeredZone& layer) {
- iterator it;
- bool success;
- std::tie(it, success) = std::set::insert(layer);
-
- if (success) {
- _map.emplace(it->id, it);
- }
-
- return { it, success };
-}
-
-void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone) {
- bool isVisible = zone->isVisible();
-
- if (empty() && isVisible) {
- // there are no zones: set this one
- insert(zone);
- return;
- } else {
- LayeredZone zoneLayer(zone);
-
- // find this zone's layer, if it exists
- iterator layer = end();
- auto it = _map.find(zoneLayer.id);
- if (it != _map.end()) {
- layer = it->second;
- // if the volume changed, we need to resort the layer (reinsertion)
- // if the visibility changed, we need to erase the layer
- if (zoneLayer.volume != layer->volume || !isVisible) {
- erase(layer);
- _map.erase(it);
- layer = end();
- }
- }
-
- // (re)insert this zone's layer if necessary
- if (layer == end() && isVisible) {
- std::tie(layer, std::ignore) = insert(zoneLayer);
- _map.emplace(layer->id, layer);
- }
- }
-}
-
-bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
- bool result = std::equal(other.begin(), other._skyboxLayer, begin());
- if (result) {
- // if valid, set the _skyboxLayer from the other LayeredZones
- _skyboxLayer = std::next(begin(), std::distance(other.begin(), other._skyboxLayer));
- }
- return result;
}
CalculateEntityLoadingPriority EntityTreeRenderer::_calculateEntityLoadingPriorityFunc = [](const EntityItem& item) -> float {
@@ -1293,14 +1279,7 @@ CalculateEntityLoadingPriority EntityTreeRenderer::_calculateEntityLoadingPriori
};
std::pair EntityTreeRenderer::getZoneInteractionProperties() {
- for (auto& zone : _layeredZones) {
- // Only domain entities control flying allowed and ghosting allowed
- if (zone.zone && zone.zone->isDomainEntity()) {
- return { zone.zone->getFlyingAllowed(), zone.zone->getGhostingAllowed() };
- }
- }
-
- return { true, true };
+ return _layeredZones.getZoneInteractionProperties();
}
bool EntityTreeRenderer::wantsKeyboardFocus(const EntityItemID& id) const {
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index cee91ad1c7..f4284078a3 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -169,7 +169,7 @@ private:
void resetEntitiesScriptEngine();
- bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr);
+ void findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar);
bool applyLayeredZones();
void stopDomainAndNonOwnedEntities();
@@ -180,13 +180,14 @@ private:
EntityItemID _currentClickingOnEntityID;
QScriptValueList createEntityArgs(const EntityItemID& entityID);
- bool checkEnterLeaveEntities();
+ void checkEnterLeaveEntities();
void leaveDomainAndNonOwnedEntities();
void leaveAllEntities();
void forceRecheckEntities();
glm::vec3 _avatarPosition { 0.0f };
- QVector _currentEntitiesInside;
+ bool _forceRecheckEntities { true };
+ QSet _currentEntitiesInside;
bool _wantScripts;
ScriptEnginePointer _entitiesScriptEngine;
@@ -209,48 +210,38 @@ private:
class LayeredZone {
public:
- LayeredZone(std::shared_ptr zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {}
- LayeredZone(std::shared_ptr zone) : LayeredZone(zone, zone->getID(), zone->getVolumeEstimate()) {}
+ LayeredZone(std::shared_ptr zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {}
- bool operator<(const LayeredZone& r) const { return std::tie(volume, id) < std::tie(r.volume, r.id); }
- bool operator==(const LayeredZone& r) const { return id == r.id; }
- bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); }
+ bool operator>(const LayeredZone& r) const { return volume > r.volume; }
+ bool operator==(const LayeredZone& r) const { return zone.lock() == r.zone.lock(); }
+ bool operator!=(const LayeredZone& r) const { return !(*this == r); }
+ bool operator>=(const LayeredZone& r) const { return (*this > r) || (*this == r); }
- std::shared_ptr zone;
+ std::weak_ptr zone;
QUuid id;
float volume;
};
- class LayeredZones : public std::set {
+ class LayeredZones : public std::priority_queue, std::greater> {
public:
- LayeredZones() {};
- LayeredZones(LayeredZones&& other);
+ void clear() { *this = LayeredZones(); }
+ bool clearDomainAndNonOwnedZones(const QUuid& sessionUUID);
- // avoid accidental misconstruction
- LayeredZones(const LayeredZones&) = delete;
- LayeredZones& operator=(const LayeredZones&) = delete;
- LayeredZones& operator=(LayeredZones&&) = delete;
+ bool equals(const LayeredZones& other) const;
+ void remove(const std::shared_ptr& zone);
+ void update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer);
- void clear();
- void clearNonLocalLayeredZones();
- std::pair insert(const LayeredZone& layer);
- void update(std::shared_ptr zone);
- bool contains(const LayeredZones& other);
-
- std::shared_ptr getZone() { return empty() ? nullptr : begin()->zone; }
-
- private:
- std::map _map;
- iterator _skyboxLayer { end() };
+ void appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const;
+ std::pair getZoneInteractionProperties() const;
};
LayeredZones _layeredZones;
- float _avgRenderableUpdateCost { 0.0f };
-
uint64_t _lastZoneCheck { 0 };
const uint64_t ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz
const float ZONE_CHECK_DISTANCE = 0.001f;
+ float _avgRenderableUpdateCost { 0.0f };
+
ReadWriteLockable _changedEntitiesGuard;
std::unordered_set _changedEntities;
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
index 64cca404cb..e4300cca76 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
@@ -92,9 +92,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
}
{ // Sun
- // Need an update ?
if (_needSunUpdate) {
- // Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight);
} else {
@@ -107,9 +105,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ // Ambient
updateAmbientMap();
- // Need an update ?
if (_needAmbientUpdate) {
- // Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_ambientIndex)) {
_ambientIndex = _stage->addLight(_ambientLight);
} else {
@@ -123,7 +119,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
updateSkyboxMap();
if (_needBackgroundUpdate) {
- if (_skyboxMode == COMPONENT_MODE_ENABLED && BackgroundStage::isIndexInvalid(_backgroundIndex)) {
+ if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundIndex = _backgroundStage->addBackground(_background);
}
_needBackgroundUpdate = false;
@@ -186,24 +182,19 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
}
}
-void ZoneEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& transaction) {
-#if 0
- if (_model) {
- _model->removeFromScene(scene, transaction);
- }
-#endif
- Parent::removeFromScene(scene, transaction);
-}
-
void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
- DependencyManager::get()->updateZone(entity->getID());
-
auto position = entity->getWorldPosition();
auto rotation = entity->getWorldOrientation();
auto dimensions = entity->getScaledDimensions();
bool rotationChanged = rotation != _lastRotation;
bool transformChanged = rotationChanged || position != _lastPosition || dimensions != _lastDimensions;
+ auto visible = entity->getVisible();
+ if (transformChanged || visible != _lastVisible) {
+ _lastVisible = visible;
+ DependencyManager::get()->updateZone(entity->getID());
+ }
+
auto proceduralUserData = entity->getUserData();
bool proceduralUserDataChanged = _proceduralUserData != proceduralUserData;
@@ -226,25 +217,6 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
_proceduralUserData = entity->getUserData();
}
-#if 0
- if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) {
- _lastShapeURL = _typedEntity->getCompoundShapeURL();
- _model.reset();
- _model = std::make_shared();
- _model->setIsWireframe(true);
- _model->init();
- _model->setURL(_lastShapeURL);
- }
-
- if (_model && _model->isActive()) {
- _model->setScaleToFit(true, _lastDimensions);
- _model->setSnapModelToRegistrationPoint(true, _entity->getRegistrationPoint());
- _model->setRotation(_lastRotation);
- _model->setTranslation(_lastPosition);
- _model->simulate(0.0f);
- }
-#endif
-
updateKeyZoneItemFromEntity(entity);
if (keyLightChanged) {
@@ -296,6 +268,10 @@ ItemKey ZoneEntityRenderer::getKey() {
}
bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
+ if (entity->getVisible() != _lastVisible) {
+ return true;
+ }
+
if (entity->keyLightPropertiesChanged() ||
entity->ambientLightPropertiesChanged() ||
entity->hazePropertiesChanged() ||
@@ -323,25 +299,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
-#if 0
- if (_typedEntity->getCompoundShapeURL() != _lastShapeURL) {
- return true;
- }
-
- if (_model) {
- if (!_model->needsFixupInScene() && (!ZoneEntityItem::getDrawZoneBoundaries() || _entity->getShapeType() != SHAPE_TYPE_COMPOUND)) {
- return true;
- }
-
- if (_model->needsFixupInScene() && (ZoneEntityItem::getDrawZoneBoundaries() || _entity->getShapeType() == SHAPE_TYPE_COMPOUND)) {
- return true;
- }
-
- if (_lastModelActive != _model->isActive()) {
- return true;
- }
- }
-#endif
+ // FIXME: do we need to trigger an update when shapeType changes? see doRenderUpdateAsynchronousTyped
return false;
}
@@ -450,7 +408,6 @@ void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& e
}
void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) {
- // nothing change if nothing change
if (_ambientTextureURL == ambientUrl) {
return;
}
@@ -466,8 +423,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) {
_pendingAmbientTexture = true;
auto textureCache = DependencyManager::get();
_ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::AMBIENT_TEXTURE);
-
- // keep whatever is assigned on the ambient map/sphere until texture is loaded
}
}
@@ -492,7 +447,6 @@ void ZoneEntityRenderer::updateAmbientMap() {
}
void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) {
- // nothing change if nothing change
if (_skyboxTextureURL == skyboxUrl) {
return;
}
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
index 2fa55f1540..5fd9b87408 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
@@ -24,9 +24,6 @@
#include "RenderableEntityItem.h"
#include
-#if 0
-#include
-#endif
namespace render { namespace entities {
class ZoneEntityRenderer : public TypedEntityRenderer {
@@ -40,7 +37,6 @@ protected:
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
virtual ItemKey getKey() override;
virtual void doRender(RenderArgs* args) override;
- virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
@@ -76,14 +72,7 @@ private:
glm::vec3 _lastPosition;
glm::vec3 _lastDimensions;
glm::quat _lastRotation;
-
- // FIXME compount shapes are currently broken
- // FIXME draw zone boundaries are currently broken (also broken in master)
-#if 0
- ModelPointer _model;
- bool _lastModelActive { false };
- QString _lastShapeURL;
-#endif
+ bool _lastVisible;
LightStagePointer _stage;
const graphics::LightPointer _sunLight { std::make_shared() };
@@ -137,25 +126,4 @@ private:
} } // namespace
-#if 0
-
-class NetworkGeometry;
-class KeyLightPayload;
-
-class RenderableZoneEntityItemMeta;
-
-class RenderableZoneEntityItem : public ZoneEntityItem, public RenderableEntityInterface {
-public:
- virtual bool contains(const glm::vec3& point) const override;
- virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
- virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
-private:
- virtual void locationChanged(bool tellPhysics = true, bool tellChildren = true) override { EntityItem::locationChanged(tellPhysics, tellChildren); notifyBoundChanged(); }
- virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
- void notifyBoundChanged();
- void notifyChangedRenderItem();
- void sceneUpdateRenderItemFromEntity(render::Transaction& transaction);
-};
-#endif
-
#endif // hifi_RenderableZoneEntityItem_h
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index e1ede9192a..7cc35f8be0 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -2023,9 +2023,10 @@ void EntityItem::setCollisionMask(uint16_t value) {
void EntityItem::setDynamic(bool value) {
if (getDynamic() != value) {
+ auto shapeType = getShapeType();
withWriteLock([&] {
// dynamic and STATIC_MESH are incompatible so we check for that case
- if (value && getShapeType() == SHAPE_TYPE_STATIC_MESH) {
+ if (value && shapeType == SHAPE_TYPE_STATIC_MESH) {
if (_dynamic) {
_dynamic = false;
_flags |= Simulation::DIRTY_MOTION_TYPE;
diff --git a/libraries/fbx/src/FBXSerializer_Node.cpp b/libraries/fbx/src/FBXSerializer_Node.cpp
index f9ef84c6f2..717bc113dd 100644
--- a/libraries/fbx/src/FBXSerializer_Node.cpp
+++ b/libraries/fbx/src/FBXSerializer_Node.cpp
@@ -41,8 +41,14 @@ QVariant readBinaryArray(QDataStream& in, int& position) {
quint32 compressedLength;
in >> arrayLength;
+ if (arrayLength > std::numeric_limits::max() / sizeof(T)) { // Upcoming byte containers are limited to max signed int
+ throw QString("FBX file most likely corrupt: binary data exceeds data limits");
+ }
in >> encoding;
in >> compressedLength;
+ if (compressedLength > std::numeric_limits::max() / sizeof(T)) { // Upcoming byte containers are limited to max signed int
+ throw QString("FBX file most likely corrupt: compressed binary data exceeds data limits");
+ }
position += sizeof(quint32) * 3;
QVector values;
diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 622fb92ce7..9952072668 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -874,6 +874,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
joint.isSkeletonJoint = false;
hfmModel.joints.push_back(joint);
}
+ hfmModel.shapeVertices.resize(hfmModel.joints.size());
// Build skeleton
@@ -1243,6 +1244,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
}
}
+ for (int clusterIndex = 0; clusterIndex < mesh.clusters.size() - 1; clusterIndex++) {
+ ShapeVertices& points = hfmModel.shapeVertices.at(clusterIndex);
+ for (glm::vec3 vertex : mesh.vertices) {
+ points.push_back(vertex);
+ }
+ }
+
mesh.meshExtents.reset();
foreach(const glm::vec3& vertex, mesh.vertices) {
mesh.meshExtents.addPoint(vertex);
diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
index 3383d71a52..0a6c76e456 100755
--- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
+++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
@@ -219,9 +219,10 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
/**jsdoc
* The Controller.Hardware.Keyboard object has properties representing keyboard, mouse, and display touch
- * events. The property values are integer IDs, uniquely identifying each output. Read-only. These can be mapped to
- * actions or functions or Controller.Standard items in a {@link RouteObject} mapping. For presses, each data
- * value is either 1.0 for "true" or 0.0 for "false".
+ * events. The property values are integer IDs, uniquely identifying each output. Read-only.
+ * These events can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject}
+ * mapping. For presses, each data value is either 1.0 for "true" or 0.0 for "false".
+ *
*
*
* Property | Type | Data | Description |
@@ -269,14 +270,18 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
* new x-coordinate value.
* MouseY | number | number | The mouse y-coordinate changed. The data value is its
* new y-coordinate value. |
- * MouseWheelRight | number | number | The mouse wheel rotated left. The data value
+ * | MouseWheelRight | number | number | The mouse wheel rotated right. The data value
* is the number of units rotated (typically 1.0 ). |
* MouseWheelLeft | number | number | The mouse wheel rotated left. The data value
* is the number of units rotated (typically 1.0 ). |
* MouseWheelUp | number | number | The mouse wheel rotated up. The data value
- * is the number of units rotated (typically 1.0 ). |
+ * is the number of units rotated (typically 1.0 ).
+ * Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of
+ * up/down.
* MouseWheelDown | number | number | The mouse wheel rotated down. The data value
- * is the number of units rotated (typically 1.0 ). |
+ * is the number of units rotated (typically 1.0 ).
+ * Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of
+ * up/down.
* TouchpadRight | number | number | The average touch on a touch-enabled device
* moved right. The data value is how far the average position of all touch points moved. |
* TouchpadLeft | number | number | The average touch on a touch-enabled device
@@ -288,7 +293,6 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
*
* |
* @typedef {object} Controller.Hardware-Keyboard
- * @todo Currently, the mouse wheel in an ordinary mouse generates left/right wheel events instead of up/down.
*/
controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const {
using namespace controller;
diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp
index 1f190111f2..25689e8bd3 100644
--- a/libraries/midi/src/Midi.cpp
+++ b/libraries/midi/src/Midi.cpp
@@ -187,38 +187,43 @@ void Midi::MidiSetup() {
MIDIINCAPS incaps;
for (unsigned int i = 0; i < midiInGetNumDevs(); i++) {
- midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS));
+ if (MMSYSERR_NOERROR == midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS))) {
- bool found = false;
- for (int j = 0; j < midiInExclude.size(); j++) {
- if (midiInExclude[j].toStdString().compare(incaps.szPname) == 0) {
- found = true;
- break;
+ bool found = false;
+ for (int j = 0; j < midiInExclude.size(); j++) {
+ if (midiInExclude[j].toStdString().compare(incaps.szPname) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) { // EXCLUDE AN INPUT BY NAME
+ HMIDIIN tmphin;
+ if (MMSYSERR_NOERROR == midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION)) {
+ if (MMSYSERR_NOERROR == midiInStart(tmphin)) {
+ midihin.push_back(tmphin);
+ }
+ }
}
- }
- if (!found) { // EXCLUDE AN INPUT BY NAME
- HMIDIIN tmphin;
- midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION);
- midiInStart(tmphin);
- midihin.push_back(tmphin);
}
}
MIDIOUTCAPS outcaps;
for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) {
- midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS));
+ if (MMSYSERR_NOERROR == midiOutGetDevCaps(i, &outcaps, sizeof(MIDIOUTCAPS))) {
- bool found = false;
- for (int j = 0; j < midiOutExclude.size(); j++) {
- if (midiOutExclude[j].toStdString().compare(outcaps.szPname) == 0) {
- found = true;
- break;
+ bool found = false;
+ for (int j = 0; j < midiOutExclude.size(); j++) {
+ if (midiOutExclude[j].toStdString().compare(outcaps.szPname) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) { // EXCLUDE AN OUTPUT BY NAME
+ HMIDIOUT tmphout;
+ if (MMSYSERR_NOERROR == midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION)) {
+ midihout.push_back(tmphout);
+ }
}
- }
- if (!found) { // EXCLUDE AN OUTPUT BY NAME
- HMIDIOUT tmphout;
- midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION);
- midihout.push_back(tmphout);
}
}
@@ -261,6 +266,61 @@ void Midi::MidiCleanup() {
}
#endif
+/**jsdoc
+ * A MIDI message.
+ * Warning: The status property is NOT a MIDI status value.
+ * @typedef {object} Midi.MidiMessage
+ * @property {number} device - Device number.
+ * @property {Midi.RawMidiMessage} raw - Raw MIDI message.
+ * @property {number} status - Channel + status. Legacy value.
+ * @property {number} channel - Channel: 1 – 16 .
+ * @property {number} type - Status: {@link Midi.MidiStatus}; 8 – 15 .
+ * @property {number} note - Note: 0 – 127 .
+ * @property {number} velocity - Note velocity: 0 – 127 . (0 means "note off".)
+ * @property {number} bend - Pitch bend: -8192 – 8191 .
+ * @property {number} program - Program change: 0 – 127 .
+ */
+/**jsdoc
+ * An integer DWORD (unsigned 32 bit) message with bits having values as follows:
+ *
+ *
+ *
+ * 00000000 |
+ * 0vvvvvvv |
+ * 0nnnnnnn |
+ * 1sss |
+ * cccc |
+ *
+ *
+ * Where:
+ *
+ * v = Velocity.
+ * n = Note.
+ * s = Status - {@link Midi.MidiStatus}
+ * c = Channel.
+ *
+ * The number in the first bit of each byte denotes whether it is a command (1) or data (0).
+ * @typedef {number} Midi.RawMidiMessage
+ */
+/**jsdoc
+ * A MIDI status value. The following MIDI status values are supported:
+ *
+ *
+ * Value | Description |
+ *
+ *
+ * 8 | Note off. |
+ * 9 | Note on. |
+ * 10 | Polyphonic key pressure. |
+ * 11 | Control change. |
+ * 12 | Program change. |
+ * 13 | Channel pressure. |
+ * 14 | Pitch bend. |
+ * 15 | System message. |
+ *
+ *
+ * @typedef {number} Midi.MidiStatus
+ */
void Midi::midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program) {
QVariantMap eventData;
eventData["device"] = device;
diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h
index 081a44f7b6..dd2b5fd678 100644
--- a/libraries/midi/src/Midi.h
+++ b/libraries/midi/src/Midi.h
@@ -21,6 +21,12 @@
#include
/**jsdoc
+ * The Midi API provides the ability to connect Interface with musical instruments and other external or virtual
+ * devices via the MIDI protocol. For further information and examples, see the tutorial:
+ * Use MIDI to Control Your Environment.
+ *
+ * Note: Only works on Windows.
+ *
* @namespace Midi
*
* @hifi-interface
@@ -49,88 +55,112 @@ private:
void MidiCleanup();
signals:
- void midiNote(QVariantMap eventData);
- void midiMessage(QVariantMap eventData);
- void midiReset();
-
- public slots:
/**jsdoc
- * Send Raw MIDI packet to a particular device.
+ * Triggered when a connected device sends an output.
+ * @function Midi.midiNote
+ * @param {Midi.MidiMessage} message - The MIDI message.
+ * @returns {Signal}
+ * @deprecated This signal is deprecated and will be removed. Use {@link Midi.midiMessage|midiMessage} instead.
+ */
+ void midiNote(QVariantMap eventData);
+
+ /**jsdoc
+ * Triggered when a connected device sends an output.
+ * @function Midi.midiMessage
+ * @param {Midi.MidiMessage} message - The MIDI message.
+ * @returns {Signal}
+ */
+ void midiMessage(QVariantMap eventData);
+
+ /**jsdoc
+ * Triggered when the system detects there was a reset such as when a device is plugged in or unplugged.
+ * @function Midi.midiReset
+ * @returns {Signal}
+ */
+ void midiReset();
+
+public slots:
+
+ /**jsdoc
+ * Sends a raw MIDI packet to a particular device.
* @function Midi.sendRawDword
* @param {number} device - Integer device number.
- * @param {number} raw - Integer (DWORD) raw MIDI message.
+ * @param {Midi.RawMidiMessage} raw - Raw MIDI message.
*/
Q_INVOKABLE void sendRawDword(int device, int raw);
/**jsdoc
- * Send MIDI message to a particular device.
+ * Sends a MIDI message to a particular device.
* @function Midi.sendMidiMessage
* @param {number} device - Integer device number.
* @param {number} channel - Integer channel number.
- * @param {number} type - 0x8 is note off, 0x9 is note on (if velocity=0, note off), etc.
- * @param {number} note - MIDI note number.
- * @param {number} velocity - Note velocity (0 means note off).
+ * @param {Midi.MidiStatus} type - Integer status value.
+ * @param {number} note - Note number.
+ * @param {number} velocity - Note velocity. (0 means "note off".)
+ * @comment The "type" parameter has that name to match up with {@link Midi.MidiMessage}.
*/
Q_INVOKABLE void sendMidiMessage(int device, int channel, int type, int note, int velocity);
/**jsdoc
- * Play a note on all connected devices.
+ * Plays a note on all connected devices.
* @function Midi.playMidiNote
- * @param {number} status - 0x80 is note off, 0x90 is note on (if velocity=0, note off), etc.
- * @param {number} note - MIDI note number.
- * @param {number} velocity - Note velocity (0 means note off).
+ * @param {MidiStatus} status - Note status.
+ * @param {number} note - Note number.
+ * @param {number} velocity - Note velocity. (0 means "note off".)
*/
Q_INVOKABLE void playMidiNote(int status, int note, int velocity);
/**jsdoc
- * Turn off all notes on all connected devices.
+ * Turns off all notes on all connected MIDI devices.
* @function Midi.allNotesOff
*/
Q_INVOKABLE void allNotesOff();
/**jsdoc
- * Clean up and re-discover attached devices.
+ * Cleans up and rediscovers attached MIDI devices.
* @function Midi.resetDevices
*/
Q_INVOKABLE void resetDevices();
/**jsdoc
- * Get a list of inputs/outputs.
+ * Gets a list of MIDI input or output devices.
* @function Midi.listMidiDevices
- * @param {boolean} output
+ * @param {boolean} output - true to list output devices, false to list input devices.
* @returns {string[]}
*/
Q_INVOKABLE QStringList listMidiDevices(bool output);
/**jsdoc
- * Block an input/output by name.
+ * Blocks a MIDI device's input or output.
* @function Midi.blockMidiDevice
- * @param {string} name
- * @param {boolean} output
+ * @param {string} name - The name of the MIDI device to block.
+ * @param {boolean} output - true to block the device's output, false to block its input.
*/
Q_INVOKABLE void blockMidiDevice(QString name, bool output);
/**jsdoc
- * Unblock an input/output by name.
+ * Unblocks a MIDI device's input or output.
* @function Midi.unblockMidiDevice
- * @param {string} name
- * @param {boolean} output
+ * @param {string} name- The name of the MIDI device to unblock.
+ * @param {boolean} output - true to unblock the device's output, false to unblock its input.
*/
Q_INVOKABLE void unblockMidiDevice(QString name, bool output);
/**jsdoc
- * Repeat all incoming notes to all outputs (default disabled).
+ * Enables or disables repeating all incoming notes to all outputs. (Default is disabled.)
* @function Midi.thruModeEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable repeating all incoming notes to all output, false to
+ * disable.
*/
Q_INVOKABLE void thruModeEnable(bool enable);
/**jsdoc
- * Broadcast on all unblocked devices.
+ * Enables or disables broadcasts to all unblocked devices.
* @function Midi.broadcastEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to have "send" functions broadcast to all devices, false to
+ * have them send to specific output devices.
*/
Q_INVOKABLE void broadcastEnable(bool enable);
@@ -138,50 +168,58 @@ signals:
/// filter by event types
/**jsdoc
+ * Enables or disables note off events.
* @function Midi.typeNoteOffEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeNoteOffEnable(bool enable);
/**jsdoc
+ * Enables or disables note on events.
* @function Midi.typeNoteOnEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeNoteOnEnable(bool enable);
/**jsdoc
+ * Enables or disables poly key pressure events.
* @function Midi.typePolyKeyPressureEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typePolyKeyPressureEnable(bool enable);
/**jsdoc
+ * Enables or disables control change events.
* @function Midi.typeControlChangeEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeControlChangeEnable(bool enable);
/**jsdoc
+ * Enables or disables program change events.
* @function Midi.typeProgramChangeEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeProgramChangeEnable(bool enable);
/**jsdoc
+ * Enables or disables channel pressure events.
* @function Midi.typeChanPressureEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeChanPressureEnable(bool enable);
/**jsdoc
+ * Enables or disables pitch bend events.
* @function Midi.typePitchBendEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typePitchBendEnable(bool enable);
/**jsdoc
+ * Enables or disables system message events.
* @function Midi.typeSystemMessageEnable
- * @param {boolean} enable
+ * @param {boolean} enable - true to enable, false to disable.
*/
Q_INVOKABLE void typeSystemMessageEnable(bool enable);
diff --git a/libraries/networking/src/udt/CongestionControl.cpp b/libraries/networking/src/udt/CongestionControl.cpp
index c0ad89e804..37b0165186 100644
--- a/libraries/networking/src/udt/CongestionControl.cpp
+++ b/libraries/networking/src/udt/CongestionControl.cpp
@@ -29,7 +29,7 @@ void CongestionControl::setMaxBandwidth(int maxBandwidth) {
void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
Q_ASSERT_X(newSendPeriod >= 0, "CongestionControl::setPacketPeriod", "Can not set a negative packet send period");
- auto packetsPerSecond = (double)_maxBandwidth / (BITS_PER_BYTE * _mss);
+ auto packetsPerSecond = _mss > 0 ? (double)_maxBandwidth / (BITS_PER_BYTE * _mss) : -1.0;
if (packetsPerSecond > 0.0) {
// anytime the packet send period is about to be increased, make sure it stays below the minimum period,
// calculated based on the maximum desired bandwidth
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 861774b145..f8574b3b94 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
- return static_cast(AvatarMixerPacketVersion::HandControllerSection);
+ return static_cast(AvatarMixerPacketVersion::SendVerificationFailed);
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
- return static_cast(AvatarMixerPacketVersion::HandControllerSection);
+ return static_cast(AvatarMixerPacketVersion::SendVerificationFailed);
case PacketType::MessagesData:
return static_cast(MessageDataVersion::TextOrBinaryData);
// ICE packets
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 274c34a268..ed81a6d9ca 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -269,6 +269,7 @@ enum class EntityVersion : PacketVersion {
CertificateTypeProperty,
DisableWebMedia,
ParticleShapeType,
+ ParticleShapeTypeDeadlockFix,
// Add new versions above here
NUM_PACKET_TYPE,
@@ -333,6 +334,7 @@ enum class AvatarMixerPacketVersion : PacketVersion {
SendMaxTranslationDimension,
FBXJointOrderChange,
HandControllerSection,
+ SendVerificationFailed
};
enum class DomainConnectRequestVersion : PacketVersion {
diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp
index 97c222337b..a1edfd6789 100644
--- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp
+++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp
@@ -31,6 +31,10 @@ bool RenderEventHandler::event(QEvent* e) {
onRender();
return true;
+ case OffscreenEvent::RenderSync:
+ onRenderSync();
+ return true;
+
case OffscreenEvent::Initialize:
onInitalize();
return true;
@@ -106,6 +110,14 @@ void RenderEventHandler::resize() {
}
void RenderEventHandler::onRender() {
+ qmlRender(false);
+}
+
+void RenderEventHandler::onRenderSync() {
+ qmlRender(true);
+}
+
+void RenderEventHandler::qmlRender(bool sceneGraphSync) {
if (_shared->isQuit()) {
return;
}
@@ -117,7 +129,8 @@ void RenderEventHandler::onRender() {
PROFILE_RANGE(render_qml_gl, __FUNCTION__);
gl::globalLock();
- if (!_shared->preRender()) {
+ if (!_shared->preRender(sceneGraphSync)) {
+ gl::globalRelease();
return;
}
diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.h b/libraries/qml/src/qml/impl/RenderEventHandler.h
index 86046c3721..139f062d0d 100644
--- a/libraries/qml/src/qml/impl/RenderEventHandler.h
+++ b/libraries/qml/src/qml/impl/RenderEventHandler.h
@@ -25,6 +25,7 @@ public:
enum Type {
Initialize = QEvent::User + 1,
Render,
+ RenderSync,
Quit
};
@@ -45,6 +46,8 @@ private:
void onInitalize();
void resize();
void onRender();
+ void onRenderSync();
+ void qmlRender(bool sceneGraphSync);
void onQuit();
SharedObject* const _shared;
@@ -59,4 +62,4 @@ private:
}}} // namespace hifi::qml::impl
-#endif
\ No newline at end of file
+#endif
diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp
index a064be79bd..b72f37481b 100644
--- a/libraries/qml/src/qml/impl/SharedObject.cpp
+++ b/libraries/qml/src/qml/impl/SharedObject.cpp
@@ -344,17 +344,17 @@ void SharedObject::setSize(const QSize& size) {
#endif
}
-bool SharedObject::preRender() {
+bool SharedObject::preRender(bool sceneGraphSync) {
#ifndef DISABLE_QML
QMutexLocker lock(&_mutex);
if (_paused) {
- if (_syncRequested) {
+ if (sceneGraphSync) {
wake();
}
return false;
}
- if (_syncRequested) {
+ if (sceneGraphSync) {
bool syncResult = true;
if (!nsightActive()) {
PROFILE_RANGE(render_qml_gl, "sync")
@@ -364,7 +364,6 @@ bool SharedObject::preRender() {
if (!syncResult) {
return false;
}
- _syncRequested = false;
}
#endif
@@ -475,9 +474,10 @@ void SharedObject::onRender() {
lock.unlock();
_renderControl->polishItems();
lock.relock();
- QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render));
+ QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::RenderSync));
// sync and render request, main and render threads must be synchronized
wait();
+ _syncRequested = false;
} else {
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render));
}
diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h
index ce9fcd46d2..c9c0ef7bd0 100644
--- a/libraries/qml/src/qml/impl/SharedObject.h
+++ b/libraries/qml/src/qml/impl/SharedObject.h
@@ -71,7 +71,7 @@ public:
private:
bool event(QEvent* e) override;
- bool preRender();
+ bool preRender(bool sceneGraphSync);
void shutdownRendering(OffscreenGLCanvas& canvas, const QSize& size);
// Called by the render event handler, from the render thread
void initializeRenderControl(QOpenGLContext* context);
diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp
index 7cad337dd5..c700f1ad3f 100644
--- a/libraries/render-utils/src/CauterizedModel.cpp
+++ b/libraries/render-utils/src/CauterizedModel.cpp
@@ -237,9 +237,11 @@ void CauterizedModel::updateRenderItems() {
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions,
cauterizedMeshState.clusterDualQuaternions);
+ data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices,
cauterizedMeshState.clusterMatrices);
+ data.computeAdjustedLocalBound(meshState.clusterMatrices);
}
Transform renderTransform = modelTransform;
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 7be5f93a95..cb3aa76468 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -450,9 +450,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) {
_adjustedLocalBound = _localBound;
if (clusterMatrices.size() > 0) {
- _adjustedLocalBound.transform(clusterMatrices[0]);
+ _adjustedLocalBound.transform(clusterMatrices.back());
- for (int i = 1; i < (int)clusterMatrices.size(); ++i) {
+ for (int i = 0; i < (int)clusterMatrices.size() - 1; ++i) {
AABox clusterBound = _localBound;
clusterBound.transform(clusterMatrices[i]);
_adjustedLocalBound += clusterBound;
@@ -463,12 +463,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterDualQuaternions) {
_adjustedLocalBound = _localBound;
if (clusterDualQuaternions.size() > 0) {
- Transform rootTransform(clusterDualQuaternions[0].getRotation(),
- clusterDualQuaternions[0].getScale(),
- clusterDualQuaternions[0].getTranslation());
+ Transform rootTransform(clusterDualQuaternions.back().getRotation(),
+ clusterDualQuaternions.back().getScale(),
+ clusterDualQuaternions.back().getTranslation());
_adjustedLocalBound.transform(rootTransform);
- for (int i = 1; i < (int)clusterDualQuaternions.size(); ++i) {
+ for (int i = 0; i < (int)clusterDualQuaternions.size() - 1; ++i) {
AABox clusterBound = _localBound;
Transform transform(clusterDualQuaternions[i].getRotation(),
clusterDualQuaternions[i].getScale(),
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index e2d78a8d94..7d0119a0f2 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -241,8 +241,10 @@ void Model::updateRenderItems() {
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions);
+ data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices);
+ data.computeAdjustedLocalBound(meshState.clusterMatrices);
}
Transform renderTransform = modelTransform;
@@ -774,11 +776,14 @@ scriptable::ScriptableModelBase Model::getScriptableModel() {
auto& materialName = _modelMeshMaterialNames[shapeID];
result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName);
- auto mappedMaterialIter = _materialMapping.find(shapeID);
- if (mappedMaterialIter != _materialMapping.end()) {
- auto mappedMaterials = mappedMaterialIter->second;
- for (auto& mappedMaterial : mappedMaterials) {
- result.appendMaterial(mappedMaterial, shapeID, materialName);
+ {
+ std::unique_lock lock(_materialMappingMutex);
+ auto mappedMaterialIter = _materialMapping.find(shapeID);
+ if (mappedMaterialIter != _materialMapping.end()) {
+ auto mappedMaterials = mappedMaterialIter->second;
+ for (auto& mappedMaterial : mappedMaterials) {
+ result.appendMaterial(mappedMaterial, shapeID, materialName);
+ }
}
}
shapeID++;
@@ -1367,8 +1372,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
// update the world space transforms for all joints
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
updateRig(deltaTime, parentTransform);
-
- computeMeshPartLocalBounds();
}
}
@@ -1379,17 +1382,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig.updateAnimations(deltaTime, parentTransform, rigToWorldTransform);
}
-void Model::computeMeshPartLocalBounds() {
- for (auto& part : _modelMeshRenderItems) {
- const Model::MeshState& state = _meshStates.at(part->_meshIndex);
- if (_useDualQuaternionSkinning) {
- part->computeAdjustedLocalBound(state.clusterDualQuaternions);
- } else {
- part->computeAdjustedLocalBound(state.clusterMatrices);
- }
- }
-}
-
// virtual
void Model::updateClusterMatrices() {
DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices");
@@ -1569,6 +1561,13 @@ void Model::applyMaterialMapping() {
auto renderItemsKey = _renderItemKeyGlobalFlags;
PrimitiveMode primitiveMode = getPrimitiveMode();
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
+ auto modelMeshRenderItemIDs = _modelMeshRenderItemIDs;
+ auto modelMeshRenderItemShapes = _modelMeshRenderItemShapes;
+ std::unordered_map shouldInvalidatePayloadShapeKeyMap;
+
+ for (auto& shape : _modelMeshRenderItemShapes) {
+ shouldInvalidatePayloadShapeKeyMap[shape.meshIndex] = shouldInvalidatePayloadShapeKey(shape.meshIndex);
+ }
auto& materialMapping = getMaterialMapping();
for (auto& mapping : materialMapping) {
@@ -1588,7 +1587,8 @@ void Model::applyMaterialMapping() {
priorityMapPerResource[shapeID] = ++_priorityMap[shapeID];
}
- auto materialLoaded = [this, networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning]() {
+ auto materialLoaded = [this, networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning,
+ modelMeshRenderItemIDs, modelMeshRenderItemShapes, shouldInvalidatePayloadShapeKeyMap]() {
if (networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) {
return;
}
@@ -1611,12 +1611,15 @@ void Model::applyMaterialMapping() {
}
}
for (auto shapeID : shapeIDs) {
- if (shapeID < _modelMeshRenderItemIDs.size()) {
- auto itemID = _modelMeshRenderItemIDs[shapeID];
- auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
- bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
+ if (shapeID < modelMeshRenderItemIDs.size()) {
+ auto itemID = modelMeshRenderItemIDs[shapeID];
+ auto meshIndex = modelMeshRenderItemShapes[shapeID].meshIndex;
+ bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKeyMap.at(meshIndex);
graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, priorityMapPerResource.at(shapeID));
- _materialMapping[shapeID].push_back(material);
+ {
+ std::unique_lock lock(_materialMappingMutex);
+ _materialMapping[shapeID].push_back(material);
+ }
transaction.updateItem(itemID, [material, renderItemsKey,
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
data.addMaterial(material);
@@ -1744,9 +1747,9 @@ void Blender::run() {
if (_model && _model->isLoaded()) {
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
int offset = 0;
- auto meshes = _model->getHFMModel().meshes;
+ const auto& meshes = _model->getHFMModel().meshes;
int meshIndex = 0;
- foreach(const HFMMesh& mesh, meshes) {
+ for(const HFMMesh& mesh : meshes) {
auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++);
if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) {
// Not blendshaped or not initialized
@@ -1777,33 +1780,30 @@ void Blender::run() {
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
const HFMBlendshape& blendshape = mesh.blendshapes.at(i);
+ for (int j = 0; j < blendshape.indices.size(); ++j) {
+ int index = blendshape.indices.at(j);
- tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) {
- for (auto j = range.begin(); j < range.end(); j++) {
- int index = blendshape.indices.at(j);
+ auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index];
+ currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
- auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index];
- currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
-
- currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
- if (j < blendshape.tangents.size()) {
- currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient;
- }
+ currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
+ if (j < blendshape.tangents.size()) {
+ currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient;
}
- });
+ }
}
// Blendshape offsets are generrated, now let's pack it on its way to gpu
- tbb::parallel_for(tbb::blocked_range(0, (int) unpackedBlendshapeOffsets.size()), [&](const tbb::blocked_range& range) {
- auto unpacked = unpackedBlendshapeOffsets.data() + range.begin();
- auto packed = meshBlendshapeOffsets + range.begin();
- for (auto j = range.begin(); j < range.end(); j++) {
+ // FIXME it feels like we could be more effectively using SIMD here
+ {
+ auto unpacked = unpackedBlendshapeOffsets.data();
+ auto packed = meshBlendshapeOffsets;
+ for (int j = 0; j < (int)unpackedBlendshapeOffsets.size(); ++j) {
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
-
- unpacked++;
- packed++;
+ ++unpacked;
+ ++packed;
}
- });
+ }
}
}
// post the result to the ModelBlender, which will dispatch to the model if still alive
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 1431b5e3f9..7844e9bc41 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -379,6 +379,7 @@ protected:
std::unordered_map _priorityMap; // only used for materialMapping
std::unordered_map> _materialMapping; // generated during applyMaterialMapping
+ std::mutex _materialMappingMutex;
void applyMaterialMapping();
void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; }
@@ -427,7 +428,6 @@ protected:
void setScaleInternal(const glm::vec3& scale);
void snapToRegistrationPoint();
- void computeMeshPartLocalBounds();
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
/// Allow sub classes to force invalidating the bboxes
diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp
index 0e9672a7f2..e1606ba9f8 100644
--- a/libraries/render-utils/src/ZoneRenderer.cpp
+++ b/libraries/render-utils/src/ZoneRenderer.cpp
@@ -43,7 +43,9 @@ const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" };
void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& output) {
// Filter out the sorted list of zones
- const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION.c_str());
+ // FIXME: the zones in the selection are already sorted, but we're doing another sort here to pick the selected items
+ // out of `input`, which means we're also looping over the inItems an extra time.
+ const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION);
// just setup the current zone env
task.addJob("SetupZones", zoneItems);
diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp
index 16d1d49d9f..3fd52d5fa2 100644
--- a/libraries/render/src/render/Scene.cpp
+++ b/libraries/render/src/render/Scene.cpp
@@ -287,12 +287,7 @@ void Scene::processTransactionFrame(const Transaction& transaction) {
_numAllocatedItems.exchange(maxID);
}
- if (transaction.touchTransactions()) {
- std::unique_lock lock(_selectionsMutex);
-
- // resets and potential NEW items
- resetSelections(transaction._resetSelections);
- }
+ resetSelections(transaction._resetSelections);
resetHighlights(transaction._highlightResets);
removeHighlights(transaction._highlightRemoves);
@@ -392,6 +387,10 @@ void Scene::updateItems(const Transaction::Updates& transactions) {
void Scene::transitionItems(const Transaction::TransitionAdds& transactions) {
auto transitionStage = getStage(TransitionStage::getName());
+ if (!transitionStage) {
+ return;
+ }
+
for (auto& add : transactions) {
auto itemId = std::get<0>(add);
// Access the true item
@@ -433,6 +432,10 @@ void Scene::reApplyTransitions(const Transaction::TransitionReApplies& transacti
void Scene::queryTransitionItems(const Transaction::TransitionQueries& transactions) {
auto transitionStage = getStage(TransitionStage::getName());
+ if (!transitionStage) {
+ return;
+ }
+
for (auto& query : transactions) {
auto itemId = std::get<0>(query);
// Access the true item
@@ -553,11 +556,14 @@ void Scene::setItemTransition(ItemID itemId, Index transitionId) {
}
void Scene::resetItemTransition(ItemID itemId) {
+ auto transitionStage = getStage(TransitionStage::getName());
+ if (!transitionStage) {
+ return;
+ }
+
auto& item = _items[itemId];
TransitionStage::Index transitionId = item.getTransitionId();
if (!render::TransitionStage::isIndexInvalid(transitionId)) {
- auto transitionStage = getStage(TransitionStage::getName());
-
auto finishedOperators = _transitionFinishedOperatorMap[transitionId];
for (auto finishedOperator : finishedOperators) {
if (finishedOperator) {
@@ -570,35 +576,34 @@ void Scene::resetItemTransition(ItemID itemId) {
}
}
-// This function is thread safe
Selection Scene::getSelection(const Selection::Name& name) const {
std::unique_lock lock(_selectionsMutex);
auto found = _selections.find(name);
if (found == _selections.end()) {
return Selection();
} else {
- return (*found).second;
+ return found->second;
}
}
-// This function is thread safe
bool Scene::isSelectionEmpty(const Selection::Name& name) const {
std::unique_lock lock(_selectionsMutex);
auto found = _selections.find(name);
if (found == _selections.end()) {
return true;
} else {
- return (*found).second.isEmpty();
+ return found->second.isEmpty();
}
}
void Scene::resetSelections(const Transaction::SelectionResets& transactions) {
+ std::unique_lock lock(_selectionsMutex);
for (auto selection : transactions) {
auto found = _selections.find(selection.getName());
if (found == _selections.end()) {
- _selections.insert(SelectionMap::value_type(selection.getName(), selection));
+ _selections[selection.getName()] = selection;
} else {
- (*found).second = selection;
+ found->second = selection;
}
}
}
diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 08fbf33bcd..5f4a209b66 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -78,9 +78,6 @@ public:
void merge(Transaction&& transaction);
void clear();
- // Checkers if there is work to do when processing the transaction
- bool touchTransactions() const { return !_resetSelections.empty(); }
-
protected:
using Reset = std::tuple;
diff --git a/libraries/render/src/render/Selection.h b/libraries/render/src/render/Selection.h
index 2c7ce710cd..05b2395b42 100644
--- a/libraries/render/src/render/Selection.h
+++ b/libraries/render/src/render/Selection.h
@@ -45,9 +45,8 @@ namespace render {
Name _name;
ItemIDs _items;
};
- using Selections = std::vector;
- using SelectionMap = std::map;
+ using SelectionMap = std::unordered_map;
}
diff --git a/libraries/shaders/src/shaders/Shaders.h b/libraries/shaders/src/shaders/Shaders.h
index 025abf7b0b..134b2bdcf8 100644
--- a/libraries/shaders/src/shaders/Shaders.h
+++ b/libraries/shaders/src/shaders/Shaders.h
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
diff --git a/libraries/shared/src/ResourceRequestObserver.cpp b/libraries/shared/src/ResourceRequestObserver.cpp
index 608d6905c5..21f4d56173 100644
--- a/libraries/shared/src/ResourceRequestObserver.cpp
+++ b/libraries/shared/src/ResourceRequestObserver.cpp
@@ -16,6 +16,13 @@
#include
#include
+/**jsdoc
+ * Information about a resource request.
+ * @typedef {object} ResourceRequestObserver.ResourceRequest
+ * @property {string} url - The URL of the resource request.
+ * @property {number} callerId - An ID identifying the request.
+ * @property {string} extra - Extra information about the request.
+ */
void ResourceRequestObserver::update(const QUrl& requestUrl,
const qint64 callerId,
const QString& extra) {
diff --git a/libraries/shared/src/ResourceRequestObserver.h b/libraries/shared/src/ResourceRequestObserver.h
index edf3c617cb..352f01c3a5 100644
--- a/libraries/shared/src/ResourceRequestObserver.h
+++ b/libraries/shared/src/ResourceRequestObserver.h
@@ -16,7 +16,15 @@
#include "DependencyManager.h"
-
+/**jsdoc
+ * The ResourceRequestObserver API provides notifications when an observable resource request is made.
+ *
+ * @namespace ResourceRequestObserver
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-avatar
+ */
class ResourceRequestObserver : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@@ -25,5 +33,29 @@ public:
void update(const QUrl& requestUrl, const qint64 callerId = -1, const QString& extra = "");
signals:
+ /**jsdoc
+ * Triggered when an observable resource request is made.
+ * @function ResourceRequestObserver.resourceRequestEvent
+ * @param {ResourceRequestObserver.ResourceRequest} request - Information about the resource request.
+ * @returns {Signal}
+ * @example Report when a particular Clipboard.importEntities() resource request is made.
+ * ResourceRequestObserver.resourceRequestEvent.connect(function (request) {
+ * if (request.callerId === 100) {
+ * print("Resource request: " + JSON.stringify(request));
+ * }
+ * });
+ *
+ * function importEntities() {
+ * var filename = Window.browse("Import entities to clipboard", "", "*.json");
+ * if (filename) {
+ * Clipboard.importEntities(filename, true, 100);
+ * pastedEntities = Clipboard.pasteEntities(Vec3.sum(MyAvatar.position,
+ * Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })));
+ * print("Entities pasted: " + JSON.stringify(pastedEntities));
+ * }
+ * }
+ *
+ * Script.setTimeout(importEntities, 2000);
+ */
void resourceRequestEvent(QVariantMap result);
};
diff --git a/libraries/ui/src/DockWidget.cpp b/libraries/ui/src/DockWidget.cpp
new file mode 100644
index 0000000000..3bcd479d61
--- /dev/null
+++ b/libraries/ui/src/DockWidget.cpp
@@ -0,0 +1,47 @@
+//
+// DockWidget.cpp
+// libraries/ui/src
+//
+// Created by Dante Ruiz 05-07-2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include "DockWidget.h"
+
+#include "OffscreenUi.h"
+
+#include
+#include
+#include
+
+#include
+
+static void quickViewDeleter(QQuickView* quickView) {
+ quickView->deleteLater();
+}
+
+DockWidget::DockWidget(const QString& title, QWidget* parent) : QDockWidget(title, parent) {
+ auto offscreenUi = DependencyManager::get();
+ auto qmlEngine = offscreenUi->getSurfaceContext()->engine();
+ _quickView = std::shared_ptr(new QQuickView(qmlEngine, nullptr), quickViewDeleter);
+ QWidget* widget = QWidget::createWindowContainer(_quickView.get());
+ setWidget(widget);
+ QWidget* headerWidget = new QWidget();
+ setTitleBarWidget(headerWidget);
+}
+
+void DockWidget::setSource(const QUrl& url) {
+ _quickView->setSource(url);
+}
+
+QQuickItem* DockWidget::getRootItem() const {
+ return _quickView->rootObject();
+}
+
+std::shared_ptr DockWidget::getQuickView() const {
+ return _quickView;
+}
diff --git a/libraries/ui/src/DockWidget.h b/libraries/ui/src/DockWidget.h
new file mode 100644
index 0000000000..e9669264e7
--- /dev/null
+++ b/libraries/ui/src/DockWidget.h
@@ -0,0 +1,33 @@
+//
+// DockWidget.h
+// libraries/ui/src
+//
+// Created by Dante Ruiz 05-07-2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#ifndef hifi_DockWidget_h
+#define hifi_DockWidget_h
+
+#include
+#include
+
+class QQuickView;
+class QQuickItem;
+class DockWidget : public QDockWidget {
+public:
+ DockWidget(const QString& title, QWidget* parent = nullptr);
+ ~DockWidget() = default;
+
+ void setSource(const QUrl& url);
+ QQuickItem* getRootItem() const;
+ std::shared_ptr getQuickView() const;
+private:
+ std::shared_ptr _quickView;
+};
+
+#endif
diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp
index e5b0a8c542..124e25675a 100644
--- a/libraries/ui/src/MainWindow.cpp
+++ b/libraries/ui/src/MainWindow.cpp
@@ -26,6 +26,10 @@
#include
#include "ui/Logging.h"
+#include "DockWidget.h"
+
+#include
+#include
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
@@ -34,6 +38,7 @@ MainWindow::MainWindow(QWidget* parent) :
{
setAttribute(Qt::WA_NoSystemBackground);
setAcceptDrops(true);
+ setStyleSheet("QMainWindow::separator {width: 1px; border: none;}");
}
MainWindow::~MainWindow() {
diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h
index fbd48e5eb1..543f8ce9af 100644
--- a/libraries/ui/src/MainWindow.h
+++ b/libraries/ui/src/MainWindow.h
@@ -16,6 +16,7 @@
#include
+class DockWidget;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
@@ -23,11 +24,10 @@ public:
~MainWindow();
static QWindow* findMainWindow();
-
public slots:
void restoreGeometry();
void saveGeometry();
-
+
signals:
void windowGeometryChanged(QRect geometry);
void windowShown(bool shown);
@@ -42,7 +42,7 @@ protected:
virtual void changeEvent(QEvent* event) override;
virtual void dragEnterEvent(QDragEnterEvent *e) override;
virtual void dropEvent(QDropEvent *e) override;
-
+
private:
Setting::Handle _windowGeometry;
Setting::Handle _windowState;
diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp
index 2f2d38fe2a..1e6a01c187 100644
--- a/libraries/ui/src/OffscreenUi.cpp
+++ b/libraries/ui/src/OffscreenUi.cpp
@@ -252,7 +252,9 @@ private slots:
_finished = true;
auto offscreenUi = DependencyManager::get();
emit response(_result);
- offscreenUi->removeModalDialog(qobject_cast(this));
+ if (!offscreenUi.isNull()) {
+ offscreenUi->removeModalDialog(qobject_cast(this));
+ }
disconnect(_dialog);
}
};
diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp
index 8d97ff78af..14830f3f04 100644
--- a/plugins/oculus/src/OculusControllerManager.cpp
+++ b/plugins/oculus/src/OculusControllerManager.cpp
@@ -409,9 +409,10 @@ void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
}
/**jsdoc
- * The Controller.Hardware.OculusTouch object has properties representing Oculus Rift. The property values are
- * integer IDs, uniquely identifying each output. Read-only. These can be mapped to actions or functions or
- * Controller.Standard items in a {@link RouteObject} mapping.
+ * The Controller.Hardware.OculusTouch object has properties representing the Oculus Rift. The property values
+ * are integer IDs, uniquely identifying each output. Read-only.
+ * These outputs can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject}
+ * mapping.
*
*
* Property | Type | Data | Description |
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index 34ebb73fda..c21a9ae4df 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -1299,14 +1299,20 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
}
/**jsdoc
- * The Controller.Hardware.Vive object has properties representing Vive. The property values are integer IDs,
- * uniquely identifying each output. Read-only. These can be mapped to actions or functions or
- * Controller.Standard items in a {@link RouteObject} mapping.
+ * The Controller.Hardware.Vive object has properties representing the Vive. The property values are integer
+ * IDs, uniquely identifying each output. Read-only.
+ * These outputs can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject}
+ * mapping.
*
*
* Property | Type | Data | Description |
*
*
+ * Buttons |
+ * LeftApplicationMenu | number | number | Left application menu button pressed.
+ * |
+ * RightApplicationMenu | number | number | Right application menu button pressed.
+ * |
* Touch Pad (Sticks) |
* LX | number | number | Left touch pad x-axis scale. |
* LY | number | number | Left touch pad y-axis scale. |
diff --git a/prebuild.py b/prebuild.py
index 5325ca34bc..b401c94e7f 100644
--- a/prebuild.py
+++ b/prebuild.py
@@ -94,7 +94,7 @@ def parse_args():
parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution')
parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build')
parser.add_argument('--ports-path', type=str, default=defaultPortsPath)
- parser.add_argument('--ci-build', action='store_true')
+ parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None)
if True:
args = parser.parse_args()
else:
diff --git a/scripts/system/away.js b/scripts/system/away.js
index e45041139a..5180588e9d 100644
--- a/scripts/system/away.js
+++ b/scripts/system/away.js
@@ -203,7 +203,7 @@ function setAwayProperties() {
if (!wasMuted) {
Audio.muted = !Audio.muted;
}
- MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
+ MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
playAwayAnimation(); // animation is still seen by others
showOverlay();
@@ -223,8 +223,8 @@ function setAwayProperties() {
function setActiveProperties() {
isAway = false;
- if (!wasMuted) {
- Audio.muted = !Audio.muted;
+ if (Audio.muted && !wasMuted) {
+ Audio.muted = false;
}
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
stopAwayAnimation();
@@ -254,7 +254,7 @@ function setActiveProperties() {
}
function maybeGoActive(event) {
- if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
+ if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
if (!isAway && (event.text === 'ESC')) {
@@ -314,6 +314,13 @@ function setEnabled(value) {
isEnabled = value;
}
+function checkAudioToggled() {
+ if (isAway && !Audio.muted) {
+ goActive();
+ }
+}
+
+
var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";
var handleMessage = function(channel, message, sender) {
if (channel === CHANNEL_AWAY_ENABLE && sender === MyAvatar.sessionUUID) {
@@ -324,9 +331,10 @@ var handleMessage = function(channel, message, sender) {
Messages.subscribe(CHANNEL_AWAY_ENABLE);
Messages.messageReceived.connect(handleMessage);
-var maybeIntervalTimer = Script.setInterval(function(){
+var maybeIntervalTimer = Script.setInterval(function() {
maybeMoveOverlay();
maybeGoAway();
+ checkAudioToggled();
}, BASIC_TIMER_INTERVAL);
diff --git a/scripts/system/clickToAvatarApp.js b/scripts/system/clickToAvatarApp.js
new file mode 100644
index 0000000000..8024f595b5
--- /dev/null
+++ b/scripts/system/clickToAvatarApp.js
@@ -0,0 +1,7 @@
+(function () {
+ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ this.clickDownOnEntity = function (entityID, mouseEvent) {
+ tablet.loadQMLSource("hifi/AvatarApp.qml");
+ };
+}
+);
diff --git a/tools/ci-scripts/hifi_backtrace_post.py b/tools/ci-scripts/hifi_backtrace_post.py
new file mode 100644
index 0000000000..a6871ef535
--- /dev/null
+++ b/tools/ci-scripts/hifi_backtrace_post.py
@@ -0,0 +1,36 @@
+# Parameters:
+# 1 - $BACKTRACE_UPLOAD_TOKEN
+# 2 - $SYMBOLS_ARCHIVE
+# 3 - $RELEASE_NUMBER
+#
+import sys
+import urllib.request
+import urllib.parse
+
+print("Running python script to upload to BackTrace")
+
+post_headers = {}
+post_headers['Content-Type'] = 'application/json'
+post_headers['Expect'] = ''
+
+post_url = 'https://highfidelity.sp.backtrace.io:6098/post?format=symbols&token=' + sys.argv[1] + '&upload_file=' + sys.argv[2] + '&tag=' + sys.argv[3]
+
+try:
+ post_data = open(sys.argv[2], 'rb')
+except:
+ print('file ' + sys.argv[2] + ' not found')
+ exit(1)
+
+try:
+ post_request = urllib.request.Request(post_url, post_data, post_headers)
+except:
+ print('urllib.request.Request failed')
+ exit(1)
+
+try:
+ post_response = urllib.request.urlopen(post_request)
+except:
+ print('urllib.request.urlopen failed')
+ exit(1)
+
+print("Upload to BackTrace completed without errors")
\ No newline at end of file
| |