Overte C++ Documentation
CompositorHelper.h
1 //
2 // Created by Bradley Austin Davis Arnold on 2015/06/13
3 // Copyright 2015 High Fidelity, Inc.
4 // Copyright 2020 Vircadia contributors.
5 //
6 // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 //
9 
10 #ifndef hifi_display_plugins_Compositor_h
11 #define hifi_display_plugins_Compositor_h
12 
13 #include <atomic>
14 #include <cstdint>
15 
16 #include <QtCore/QObject>
17 #include <QtCore/QMutex>
18 #include <QtCore/QPropertyAnimation>
19 #include <QtGui/QCursor>
20 #include <QtGui/QMouseEvent>
21 
22 #include <GLMHelpers.h>
23 #include <Transform.h>
24 #include <DependencyManager.h>
25 
26 #include "DisplayPlugin.h"
27 
28 class ReticleInterface;
29 
30 const float HUD_RADIUS = 1.5f;
31 const float DEFAULT_RETICLE_DEPTH = HUD_RADIUS;
32 
33 const float MAGNIFY_WIDTH = 220.0f;
34 const float MAGNIFY_HEIGHT = 100.0f;
35 const float MAGNIFY_MULT = 2.0f;
36 
37 // Handles the drawing of the overlays to the screen
38 // TODO, move divide up the rendering, displaying and input handling
39 // facilities of this class
40 class CompositorHelper : public QObject, public Dependency {
41  Q_OBJECT
42 
43  Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha NOTIFY alphaChanged)
44  Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
45 public:
46  static const uvec2 VIRTUAL_SCREEN_SIZE;
47  static const QRect VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
48  static const float VIRTUAL_UI_ASPECT_RATIO;
49  static const vec2 VIRTUAL_UI_TARGET_FOV;
50  static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE;
51  static const vec2 MOUSE_EXTENTS_PIXELS;
52 
53  CompositorHelper();
54  void setRenderingWidget(QWidget* widget) { _renderingWidget = widget; }
55 
56  bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
57  bool calculateParabolaUICollisionPoint(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, glm::vec3& result, float& parabolicDistance) const;
58 
59  bool isHMD() const;
60  bool fakeEventActive() const { return _fakeMouseEvent; }
61 
62  // Converter from one frame of reference to another.
63  // Frame of reference:
64  // Spherical: Polar coordinates that gives the position on the sphere we project on (yaw,pitch)
65  // Overlay: Position on the overlay (x,y)
66  glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
67  glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
68  void computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& origin, glm::vec3& direction) const;
69 
70  glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
71  glm::vec3 sphereSurfaceFromOverlay(const glm::vec2& overlay) const;
72 
73  void setCameraBaseTransform(const Transform& transform) { _cameraBaseTransform = transform; }
74  const Transform& getCameraBaseTransform() const { return _cameraBaseTransform; }
75 
76  void setModelTransform(const Transform& transform) { _modelTransform = transform; }
77  const Transform& getModelTransform() const { return _modelTransform; }
78  glm::mat4 getUiTransform() const;
79 
80  float getAlpha() const { return _alpha; }
81  void setAlpha(float alpha) { if (alpha != _alpha) { emit alphaChanged(); _alpha = alpha; } }
82 
83  bool getReticleVisible() const { return _reticleVisible; }
84  void setReticleVisible(bool visible) { _reticleVisible = visible; }
85 
86  float getReticleDepth() const { return _reticleDepth; }
87  void setReticleDepth(float depth) { _reticleDepth = depth; }
88  void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; }
89 
90  glm::vec2 getReticlePosition() const;
91  void setReticlePosition(const glm::vec2& position, bool sendFakeEvent = true);
92 
93  glm::vec2 getReticleMaximumPosition() const;
94 
95  glm::mat4 getReticleTransform(const glm::mat4& eyePose = glm::mat4(), const glm::vec3& headPosition = glm::vec3()) const;
96  glm::mat4 getPoint2DTransform(const glm::vec2& point, float sizeX , float sizeY) const;
97 
98  ReticleInterface* getReticleInterface() { return _reticleInterface; }
99 
101  bool handleRealMouseMoveEvent(bool sendFakeEvent = true);
102  void handleLeaveEvent();
103  QPointF getMouseEventPosition(QMouseEvent* event);
104 
105  bool shouldCaptureMouse() const;
106 
107  bool getAllowMouseCapture() const { return _allowMouseCapture; }
108  void setAllowMouseCapture(bool capture);
109  void setEnableMouseCaptureVR(bool capture);
110 
112  bool getReticleOverDesktop() const;
113  void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
114 
115  void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
116  void setFrameInfo(uint32_t frame, const glm::mat4& camera, const glm::mat4& sensorToWorldMatrix) {
117  _currentCamera = camera;
118  _sensorToWorldMatrix = sensorToWorldMatrix;
119  }
120 
121 signals:
122  void allowMouseCaptureChanged();
123  void alphaChanged();
124 
125 protected slots:
126  void sendFakeMouseEvent();
127 
128 private:
129  void updateTooltips();
130 
131  DisplayPluginPointer _currentDisplayPlugin;
132  glm::mat4 _currentCamera;
133  glm::mat4 _sensorToWorldMatrix;
134  QWidget* _renderingWidget{ nullptr };
135 
137  //static EntityItemID _noItemId;
138  //EntityItemID _hoverItemId { _noItemId };
139 
140  //QString _hoverItemTitle;
141  //QString _hoverItemDescription;
142  //quint64 _hoverItemEnterUsecs { 0 };
143 
144  bool _isOverDesktop { true };
145  float _textureFov { VIRTUAL_UI_TARGET_FOV.y };
146  float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
147 
148  float _alpha { 1.0f };
149 
150  int _previousBorderWidth { -1 };
151  int _previousBorderHeight { -1 };
152 
153  Transform _modelTransform;
154  Transform _cameraBaseTransform;
155 
156  std::unique_ptr<QPropertyAnimation> _alphaPropertyAnimation;
157 
158  std::atomic<bool> _reticleVisible { true };
159  std::atomic<float> _reticleDepth { DEFAULT_RETICLE_DEPTH };
160 
161  // NOTE: when the compositor is running in HMD mode, it will control the reticle position as a custom
162  // application specific position, when it's in desktop mode, the reticle position will simply move
163  // the system mouse.
164  glm::vec2 _reticlePositionInHMD { 0.0f, 0.0f };
165  mutable QRecursiveMutex _reticleLock;
166 
167  QPointF _lastKnownRealMouse;
168  bool _ignoreMouseMove { false };
169 
170  bool _reticleOverQml { false };
171 
172  std::atomic<bool> _allowMouseCapture{ true };
173  std::atomic<bool> _enableMouseCaptureVR{ false };
174 
175  bool _fakeMouseEvent { false };
176 
177  ReticleInterface* _reticleInterface { nullptr };
178 };
179 
180 /*@jsdoc
181  * The <code>Reticle</code> API provides access to the mouse cursor. The cursor may be an arrow or a reticle circle, depending
182  * on Interface settings. The mouse cursor is visible in HMD mode if controllers aren't being used.
183  *
184  * @namespace Reticle
185  *
186  * @hifi-interface
187  * @hifi-client-entity
188  * @hifi-avatar
189  *
190  * @property {boolean} allowMouseCapture=true - <code>true</code> if the mouse cursor will be captured when in HMD mode and the
191  * Interface window content (excluding menus) has focus, <code>false</code> if the mouse cursor will not be captured.
192  * @property {number} depth - The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
193  * @property {Vec2} maximumPosition - The maximum reticle coordinates on the display device in desktop mode or the HUD surface
194  * in HMD mode. (The minimum reticle coordinates on the desktop display device or HUD surface are <code>0</code>,
195  * <code>0</code>.) <em>Read-only.</em>
196  * @property {boolean} mouseCaptured - <code>true</code> if the mouse cursor is captured, displaying only in Interface and
197  * not on the rest of the desktop. The mouse cursor may be captured when in HMD mode and the Interface window content
198  * (excluding menu items) has focus, if capturing is enabled (<code>allowMouseCapture</code> is <code>true</code>).
199  * <em>Read-only.</em>
200  * @property {boolean} pointingAtSystemOverlay - <code>true</code> if the mouse cursor is pointing at UI in the Interface
201  * window in desktop mode or on the HUD surface in HMD mode, <code>false</code> if it isn't. <em>Read-only.</em>
202  * @property {Vec2} position - The position of the cursor. This is the position relative to the Interface window in desktop
203  * mode, and the HUD surface in HMD mode.
204  * <p><strong>Note:</strong> The position values may be negative.</p>
205  * @property {number} scale=1 - The scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode.
206  * (Does not affect the size of the arrow in desktop mode.)
207  * @property {boolean} visible=true - <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or
208  * reticle circle are visible in HMD mode; <code>false</code> otherwise. (Does not affect the visibility of the mouse
209  * pointer in desktop mode.)
210  */
211 // Scripting interface available to control the Reticle
212 class ReticleInterface : public QObject {
213  Q_OBJECT
214  Q_PROPERTY(QVariant position READ getPosition WRITE setPosition)
215  Q_PROPERTY(bool visible READ getVisible WRITE setVisible)
216  Q_PROPERTY(float depth READ getDepth WRITE setDepth)
217  Q_PROPERTY(float scale READ getScale WRITE setScale)
218  Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
219  Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
220  Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture)
221  Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
222 
223 public:
224  ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {}
225 
226  /*@jsdoc
227  * Checks whether the mouse cursor is captured, displaying only in Interface and not on the rest of the desktop. The mouse
228  * cursor is captured when in HMD mode and the Interface window content (excluding menu items) has focus, if capturing is
229  * enabled (<code>allowMouseCapture</code> property value is <code>true</code>).
230  * @function Reticle.isMouseCaptured
231  * @returns {boolean} <code>true</code> if the mouse cursor is captured, displaying only in Interface and not on the
232  * desktop.
233  */
234  Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
235 
236  /*@jsdoc
237  * Gets whether the mouse cursor will be captured when in HMD mode and the Interface window content (excluding menu items)
238  * has focus. When captured, the mouse cursor displays only in Interface, not on the rest of the desktop.
239  * @function Reticle.getAllowMouseCapture
240  * @returns {boolean} <code>true</code> if the mouse cursor will be captured when in HMD mode and the Interface window
241  * content has focus, <code>false</code> if the mouse cursor will not be captured.
242  */
243  Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
244 
245  /*@jsdoc
246  * Sets whether the mouse cursor will be captured when in HMD mode and the Interface window content (excluding menu items)
247  * has focus. When captured, the mouse cursor displays only in Interface, not on the rest of desktop.
248  * @function Reticle.setAllowMouseCapture
249  * @param {boolean} allowMouseCaptured - <code>true</code> if the mouse cursor will be captured when in HMD mode and the
250  * Interface window content has focus, <code>false</code> if the mouse cursor will not be captured.
251  */
252  Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
253 
254  /*@jsdoc
255  * Gets whether the mouse cursor is pointing at UI in the Interface window in desktop mode or on the HUD surface in HMD
256  * mode.
257  * @function Reticle.isPointingAtSystemOverlay
258  * @returns {boolean} <code>true</code> if the mouse cursor is pointing at UI in the Interface window in desktop mode or on
259  * the HUD surface in HMD mode, <code>false</code> if it isn't.
260  */
261  Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
262 
263  /*@jsdoc
264  * Gets whether the reticle circle is visible in desktop mode, or the arrow or reticle circle are visible in HMD mode.
265  * @function Reticle.getVisible
266  * @returns {boolean} <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or
267  * reticle circle are visible in HMD mode; <code>false</code> otherwise. (The mouse pointer is always visible in
268  * desktop mode.)
269  */
270  Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
271 
272  /*@jsdoc
273  * Sets whether the reticle circle is visible in desktop mode, or the arrow or reticle circle are visible in HMD mode.
274  * @function Reticle.setVisible
275  * @param {boolean} visible - <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or reticle
276  * circle are visible in HMD mode; <code>false</code> otherwise. (Does not affect the visibility of the mouse pointer
277  * in desktop mode.)
278  */
279  Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); }
280 
281  /*@jsdoc
282  * Gets the depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
283  * @function Reticle.getDepth
284  * @returns {number} The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
285  */
286  Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
287 
288  /*@jsdoc
289  * Sets the depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
290  * @function Reticle.setDepth
291  * @param {number} depth - The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
292  */
293  Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
294 
295  /*@jsdoc
296  * Gets the scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode. (Does not affect the
297  * size of the arrow in desktop mode.) The default scale is <code>1.0</code>.
298  * @function Reticle.getScale
299  * @returns {number} The scale of the reticle.
300  */
301  Q_INVOKABLE float getScale() const;
302 
303  /*@jsdoc
304  * Sets the scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode. (Does not affect the
305  * size of the arrow in desktop mode.) The default scale is <code>1.0</code>.
306  * @function Reticle.setScale
307  * @param {number} scale - The scale of the reticle.
308  */
309  Q_INVOKABLE void setScale(float scale);
310 
311  /*@jsdoc
312  * Gets the position of the cursor. This is the position relative to the Interface window in desktop mode, and the HUD
313  * surface in HMD mode.
314  * <p><strong>Note:</strong> The position values may be negative.</p>
315  * @function Reticle.getPosition
316  * @returns {Vec2} The position of the cursor.
317  */
318  Q_INVOKABLE QVariant getPosition() const;
319 
320  /*@jsdoc
321  * Sets the position of the cursor. This is the position relative to the Interface window in desktop mode, and the HUD
322  * surface in HMD mode.
323  * <p><strong>Note:</strong> The position values may be negative.</p>
324  * @function Reticle.setPosition
325  * @param {Vec2} position - The position of the cursor.
326  */
327  Q_INVOKABLE void setPosition(QVariant position);
328 
329  /*@jsdoc
330  * Gets the maximum reticle coordinates on the display device in desktop mode or the HUD surface in HMD mode. (The minimum
331  * reticle coordinates on the desktop display device or HUD surface are <code>0</code>, <code>0</code>.)
332  * @function Reticle.getMaximumPosition
333  * @returns {Vec2} The maximum reticle coordinates on the display device in desktop mode or the HUD surface in HMD mode.
334  */
335  Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
336 
337 private:
338  CompositorHelper* _compositor;
339 };
340 
341 #endif // hifi_CompositorHelper_h