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 
111  bool getReticleOverDesktop() const;
112  void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
113 
114  void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
115  void setFrameInfo(uint32_t frame, const glm::mat4& camera, const glm::mat4& sensorToWorldMatrix) {
116  _currentCamera = camera;
117  _sensorToWorldMatrix = sensorToWorldMatrix;
118  }
119 
120 signals:
121  void allowMouseCaptureChanged();
122  void alphaChanged();
123 
124 protected slots:
125  void sendFakeMouseEvent();
126 
127 private:
128  void updateTooltips();
129 
130  DisplayPluginPointer _currentDisplayPlugin;
131  glm::mat4 _currentCamera;
132  glm::mat4 _sensorToWorldMatrix;
133  QWidget* _renderingWidget{ nullptr };
134 
136  //static EntityItemID _noItemId;
137  //EntityItemID _hoverItemId { _noItemId };
138 
139  //QString _hoverItemTitle;
140  //QString _hoverItemDescription;
141  //quint64 _hoverItemEnterUsecs { 0 };
142 
143  bool _isOverDesktop { true };
144  float _textureFov { VIRTUAL_UI_TARGET_FOV.y };
145  float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
146 
147  float _alpha { 1.0f };
148 
149  int _previousBorderWidth { -1 };
150  int _previousBorderHeight { -1 };
151 
152  Transform _modelTransform;
153  Transform _cameraBaseTransform;
154 
155  std::unique_ptr<QPropertyAnimation> _alphaPropertyAnimation;
156 
157  std::atomic<bool> _reticleVisible { true };
158  std::atomic<float> _reticleDepth { DEFAULT_RETICLE_DEPTH };
159 
160  // NOTE: when the compositor is running in HMD mode, it will control the reticle position as a custom
161  // application specific position, when it's in desktop mode, the reticle position will simply move
162  // the system mouse.
163  glm::vec2 _reticlePositionInHMD { 0.0f, 0.0f };
164  mutable QRecursiveMutex _reticleLock;
165 
166  QPointF _lastKnownRealMouse;
167  bool _ignoreMouseMove { false };
168 
169  bool _reticleOverQml { false };
170 
171  std::atomic<bool> _allowMouseCapture { true };
172 
173  bool _fakeMouseEvent { false };
174 
175  ReticleInterface* _reticleInterface { nullptr };
176 };
177 
178 /*@jsdoc
179  * The <code>Reticle</code> API provides access to the mouse cursor. The cursor may be an arrow or a reticle circle, depending
180  * on Interface settings. The mouse cursor is visible in HMD mode if controllers aren't being used.
181  *
182  * @namespace Reticle
183  *
184  * @hifi-interface
185  * @hifi-client-entity
186  * @hifi-avatar
187  *
188  * @property {boolean} allowMouseCapture=true - <code>true</code> if the mouse cursor will be captured when in HMD mode and the
189  * Interface window content (excluding menus) has focus, <code>false</code> if the mouse cursor will not be captured.
190  * @property {number} depth - The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
191  * @property {Vec2} maximumPosition - The maximum reticle coordinates on the display device in desktop mode or the HUD surface
192  * in HMD mode. (The minimum reticle coordinates on the desktop display device or HUD surface are <code>0</code>,
193  * <code>0</code>.) <em>Read-only.</em>
194  * @property {boolean} mouseCaptured - <code>true</code> if the mouse cursor is captured, displaying only in Interface and
195  * not on the rest of the desktop. The mouse cursor may be captured when in HMD mode and the Interface window content
196  * (excluding menu items) has focus, if capturing is enabled (<code>allowMouseCapture</code> is <code>true</code>).
197  * <em>Read-only.</em>
198  * @property {boolean} pointingAtSystemOverlay - <code>true</code> if the mouse cursor is pointing at UI in the Interface
199  * window in desktop mode or on the HUD surface in HMD mode, <code>false</code> if it isn't. <em>Read-only.</em>
200  * @property {Vec2} position - The position of the cursor. This is the position relative to the Interface window in desktop
201  * mode, and the HUD surface in HMD mode.
202  * <p><strong>Note:</strong> The position values may be negative.</p>
203  * @property {number} scale=1 - The scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode.
204  * (Does not affect the size of the arrow in desktop mode.)
205  * @property {boolean} visible=true - <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or
206  * reticle circle are visible in HMD mode; <code>false</code> otherwise. (Does not affect the visibility of the mouse
207  * pointer in desktop mode.)
208  */
209 // Scripting interface available to control the Reticle
210 class ReticleInterface : public QObject {
211  Q_OBJECT
212  Q_PROPERTY(QVariant position READ getPosition WRITE setPosition)
213  Q_PROPERTY(bool visible READ getVisible WRITE setVisible)
214  Q_PROPERTY(float depth READ getDepth WRITE setDepth)
215  Q_PROPERTY(float scale READ getScale WRITE setScale)
216  Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
217  Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
218  Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture)
219  Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
220 
221 public:
222  ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {}
223 
224  /*@jsdoc
225  * Checks whether the mouse cursor is captured, displaying only in Interface and not on the rest of the desktop. The mouse
226  * cursor is captured when in HMD mode and the Interface window content (excluding menu items) has focus, if capturing is
227  * enabled (<code>allowMouseCapture</code> property value is <code>true</code>).
228  * @function Reticle.isMouseCaptured
229  * @returns {boolean} <code>true</code> if the mouse cursor is captured, displaying only in Interface and not on the
230  * desktop.
231  */
232  Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
233 
234  /*@jsdoc
235  * Gets whether the mouse cursor will be captured when in HMD mode and the Interface window content (excluding menu items)
236  * has focus. When captured, the mouse cursor displays only in Interface, not on the rest of the desktop.
237  * @function Reticle.getAllowMouseCapture
238  * @returns {boolean} <code>true</code> if the mouse cursor will be captured when in HMD mode and the Interface window
239  * content has focus, <code>false</code> if the mouse cursor will not be captured.
240  */
241  Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
242 
243  /*@jsdoc
244  * Sets whether the mouse cursor will be captured when in HMD mode and the Interface window content (excluding menu items)
245  * has focus. When captured, the mouse cursor displays only in Interface, not on the rest of desktop.
246  * @function Reticle.setAllowMouseCapture
247  * @param {boolean} allowMouseCaptured - <code>true</code> if the mouse cursor will be captured when in HMD mode and the
248  * Interface window content has focus, <code>false</code> if the mouse cursor will not be captured.
249  */
250  Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
251 
252  /*@jsdoc
253  * Gets whether the mouse cursor is pointing at UI in the Interface window in desktop mode or on the HUD surface in HMD
254  * mode.
255  * @function Reticle.isPointingAtSystemOverlay
256  * @returns {boolean} <code>true</code> if the mouse cursor is pointing at UI in the Interface window in desktop mode or on
257  * the HUD surface in HMD mode, <code>false</code> if it isn't.
258  */
259  Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
260 
261  /*@jsdoc
262  * Gets whether the reticle circle is visible in desktop mode, or the arrow or reticle circle are visible in HMD mode.
263  * @function Reticle.getVisible
264  * @returns {boolean} <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or
265  * reticle circle are visible in HMD mode; <code>false</code> otherwise. (The mouse pointer is always visible in
266  * desktop mode.)
267  */
268  Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
269 
270  /*@jsdoc
271  * Sets whether the reticle circle is visible in desktop mode, or the arrow or reticle circle are visible in HMD mode.
272  * @function Reticle.setVisible
273  * @param {boolean} visible - <code>true</code> if the reticle circle is visible in desktop mode, and the arrow or reticle
274  * circle are visible in HMD mode; <code>false</code> otherwise. (Does not affect the visibility of the mouse pointer
275  * in desktop mode.)
276  */
277  Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); }
278 
279  /*@jsdoc
280  * Gets the depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
281  * @function Reticle.getDepth
282  * @returns {number} The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
283  */
284  Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
285 
286  /*@jsdoc
287  * Sets the depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
288  * @function Reticle.setDepth
289  * @param {number} depth - The depth (distance) that the reticle is displayed at relative to the HMD view, in HMD mode.
290  */
291  Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
292 
293  /*@jsdoc
294  * Gets the scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode. (Does not affect the
295  * size of the arrow in desktop mode.) The default scale is <code>1.0</code>.
296  * @function Reticle.getScale
297  * @returns {number} The scale of the reticle.
298  */
299  Q_INVOKABLE float getScale() const;
300 
301  /*@jsdoc
302  * Sets the scale of the reticle circle in desktop mode, and the arrow and reticle circle in HMD mode. (Does not affect the
303  * size of the arrow in desktop mode.) The default scale is <code>1.0</code>.
304  * @function Reticle.setScale
305  * @param {number} scale - The scale of the reticle.
306  */
307  Q_INVOKABLE void setScale(float scale);
308 
309  /*@jsdoc
310  * Gets the position of the cursor. This is the position relative to the Interface window in desktop mode, and the HUD
311  * surface in HMD mode.
312  * <p><strong>Note:</strong> The position values may be negative.</p>
313  * @function Reticle.getPosition
314  * @returns {Vec2} The position of the cursor.
315  */
316  Q_INVOKABLE QVariant getPosition() const;
317 
318  /*@jsdoc
319  * Sets the position of the cursor. This is the position relative to the Interface window in desktop mode, and the HUD
320  * surface in HMD mode.
321  * <p><strong>Note:</strong> The position values may be negative.</p>
322  * @function Reticle.setPosition
323  * @param {Vec2} position - The position of the cursor.
324  */
325  Q_INVOKABLE void setPosition(QVariant position);
326 
327  /*@jsdoc
328  * Gets the maximum reticle coordinates on the display device in desktop mode or the HUD surface in HMD mode. (The minimum
329  * reticle coordinates on the desktop display device or HUD surface are <code>0</code>, <code>0</code>.)
330  * @function Reticle.getMaximumPosition
331  * @returns {Vec2} The maximum reticle coordinates on the display device in desktop mode or the HUD surface in HMD mode.
332  */
333  Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
334 
335 private:
336  CompositorHelper* _compositor;
337 };
338 
339 #endif // hifi_CompositorHelper_h