Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dxrcamera.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
8#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
9#include <QtQuick3D/private/qquick3dutils_p.h>
10#include <QtQuick3D/private/qquick3dnode_p_p.h>
11
12#include <cmath>
13
14QT_BEGIN_NAMESPACE
15/*!
16 \qmltype XrCamera
17 \inherits Node
18 \inqmlmodule QtQuick3D.Xr
19 \brief Tracks spatial position and orientation from which the user views an XR scene.
20
21 The XrCamera is a tracked spatial node that tracks the spatial position and orientation
22 of the users's view of an XR scene.
23
24 Since this is a tracked node, its spatial properties should be considered read-only.
25 Properties specific to the XrCamera, such as the \l{clipNear}{near} and \l{clipFar}{far} clip planes, are settable.
26 When set, they override preferred values.
27
28 Unlike a \l PerspectiveCamera, an XrCamera has no field of view: the runtime supplies
29 the projection for each eye.
30
31 \sa XrOrigin::camera, {Coordinate Systems and Units in \qxr}
32*/
33
34QQuick3DXrEyeCamera::QQuick3DXrEyeCamera(QQuick3DXrOrigin *parent)
35 : QQuick3DCamera(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::CustomCamera, QQuick3DContentLayer::LayerAll)), parent)
36{
37}
38
39float QQuick3DXrEyeCamera::leftTangent() const
40{
41 return m_leftTangent;
42}
43
44float QQuick3DXrEyeCamera::rightTangent() const
45{
46 return m_rightTangent;
47}
48
49float QQuick3DXrEyeCamera::upTangent() const
50{
51 return m_upTangent;
52}
53
54float QQuick3DXrEyeCamera::downTangent() const
55{
56 return m_downTangent;
57}
58
59float QQuick3DXrEyeCamera::clipNear() const
60{
61 return m_clipNear;
62}
63
64float QQuick3DXrEyeCamera::clipFar() const
65{
66 return m_clipFar;
67}
68
69void QQuick3DXrEyeCamera::setLeftTangent(float leftTangent)
70{
71 if (qFuzzyCompare(m_leftTangent, leftTangent))
72 return;
73
74 m_leftTangent = leftTangent;
75 emit leftTangentChanged(m_leftTangent);
76 markDirty(DirtyFlag::ProjectionDirty);
77}
78
79void QQuick3DXrEyeCamera::setRightTangent(float rightTangent)
80{
81 if (qFuzzyCompare(m_rightTangent, rightTangent))
82 return;
83
84 m_rightTangent = rightTangent;
85 emit rightTangentChanged(m_rightTangent);
86 markDirty(DirtyFlag::ProjectionDirty);
87}
88
89void QQuick3DXrEyeCamera::setUpTangent(float upTangent)
90{
91 if (qFuzzyCompare(m_upTangent, upTangent))
92 return;
93
94 m_upTangent = upTangent;
95 emit upTangentChanged(m_upTangent);
96 markDirty(DirtyFlag::ProjectionDirty);
97}
98
99void QQuick3DXrEyeCamera::setDownTangent(float downTangent)
100{
101 if (qFuzzyCompare(m_downTangent, downTangent))
102 return;
103
104 m_downTangent = downTangent;
105 emit downTangentChanged(m_downTangent);
106 markDirty(DirtyFlag::ProjectionDirty);
107}
108
109void QQuick3DXrEyeCamera::setClipNear(float clipNear)
110{
111 if (qFuzzyCompare(m_clipNear, clipNear))
112 return;
113
114 m_clipNear = clipNear;
115 emit clipNearChanged(m_clipNear);
116 markDirty(DirtyFlag::ProjectionDirty);
117}
118
119void QQuick3DXrEyeCamera::setClipFar(float clipFar)
120{
121 if (qFuzzyCompare(m_clipFar, clipFar))
122 return;
123
124 m_clipFar = clipFar;
125 emit clipFarChanged(m_clipFar);
126 markDirty(DirtyFlag::ProjectionDirty);
127}
128
129void QQuick3DXrEyeCamera::setProjection(const QMatrix4x4 &projection)
130{
131 m_projection = projection;
132 markDirty(DirtyFlag::ProjectionChanged);
133}
134
135QSSGRenderGraphObject *QQuick3DXrEyeCamera::updateSpatialNode(QSSGRenderGraphObject *node)
136{
137 QSSGRenderCamera *camera = static_cast<QSSGRenderCamera *>(QQuick3DCamera::updateSpatialNode(node));
138 if (camera) {
139 // Projection changed takes precedence over projection dirty
140 bool changed = false;
141 if (m_dirtyFlags & DirtyFlag::ProjectionChanged) {
142 camera->projection = m_projection;
143 qUpdateIfNeeded(camera->clipPlanes, { m_clipNear, m_clipFar });
144 m_dirtyFlags &= ~DirtyFlag::ProjectionChanged;
145 changed = true;
146 } else if (m_dirtyFlags & DirtyFlag::ProjectionDirty) {
147 maybeUpdateProjection();
148 changed |= qUpdateIfNeeded(camera->projection, m_projection);
149 qUpdateIfNeeded(camera->clipPlanes, { m_clipNear, m_clipFar });
150 m_dirtyFlags &= ~DirtyFlag::ProjectionDirty;
151 }
152
153 if (m_dirtyFlags & DirtyFlag::ClipChanged) {
154 qUpdateIfNeeded(camera->clipPlanes, { m_clipNear, m_clipFar });
155 m_dirtyFlags &= ~DirtyFlag::ClipChanged;
156 }
157
158 // Reset the dirty flag after all updates have been done
159 m_dirtyFlags = 0;
160
161 if (changed)
162 camera->markDirty(QSSGRenderCamera::DirtyFlag::CameraDirty);
163 }
164 return camera;
165}
166
167void QQuick3DXrEyeCamera::markDirty(DirtyFlag flag)
168{
169 if (m_dirtyFlags & flag)
170 return;
171
172 m_dirtyFlags |= flag;
173 update();
174}
175
176void QQuick3DXrEyeCamera::maybeUpdateProjection()
177{
178 QSSG_ASSERT(m_dirtyFlags & DirtyFlag::ProjectionDirty, return);
179
180 const float right = m_rightTangent * m_clipNear;
181 const float top = m_upTangent * m_clipNear;
182#if defined(Q_OS_VISIONOS)
183 // cp_view_get_tangents always returns positive values
184 // so we need to negate the left and down tangents
185 const float left = -m_leftTangent * m_clipNear;
186 const float bottom = -m_downTangent * m_clipNear;
187#else
188 const float left = m_leftTangent * m_clipNear;
189 const float bottom = m_downTangent * m_clipNear;
190#endif
191
192 float *m = m_projection.data();
193
194 m[0] = 2 * m_clipNear / (right - left);
195 m[4] = 0;
196 m[8] = (right + left) / (right - left);
197 m[12] = 0;
198
199 m[1] = 0;
200 m[5] = 2 * m_clipNear / (top - bottom);
201 m[9] = (top + bottom) / (top - bottom);
202 m[13] = 0;
203
204 m[2] = 0;
205 m[6] = 0;
206 m[10] = m_clipFar / (m_clipNear - m_clipFar);
207 m[14] = m_clipFar * m_clipNear / (m_clipNear - m_clipFar);
208
209
210 m[3] = 0;
211 m[7] = 0;
212 m[11] = -1;
213 m[15] = 0;
214
215 const bool isReverseZ = false; // placeholder
216 if (isReverseZ) {
217 if (std::isinf(m_clipFar)) {
218 m[10] = 0;
219 m[14] = m_clipNear;
220 } else {
221 m[10] = -m[10] - 1;
222 m[14] = -m[14];
223 }
224 } else if (std::isinf(m_clipFar)) {
225 m[10] = -1;
226 m[14] = -m_clipNear;
227 }
228}
229
230QQuick3DXrCamera::QQuick3DXrCamera(QQuick3DXrOrigin *parent)
231 : QQuick3DNode(parent)
232{
233}
234
235QQuick3DXrCamera::~QQuick3DXrCamera()
236{
237}
238
239/*!
240 \qmlproperty real QtQuick3D.Xr::XrCamera::clipNear
241 \brief The start of the distance range, with reference to the camera position, in which objects will appear.
242
243 The distance is in centimeters, like every other distance in an XR scene.
244
245 \note Unless set explicitly, the clipNear value will be set to the device's preferred value.
246
247 \sa {Coordinate Systems and Units in \qxr}
248*/
249
250float QQuick3DXrCamera::clipNear() const
251{
252 return m_clipNear;
253}
254
255/*!
256 \qmlproperty real QtQuick3D.Xr::XrCamera::clipFar
257 \brief The end of the distance range, with reference to the camera position, in which objects will appear.
258
259 The distance is in centimeters, like every other distance in an XR scene.
260
261 \note Unless set explicitly, the clipFar value will be set to the device's preferred value.
262
263 \sa {Coordinate Systems and Units in \qxr}
264*/
265
266float QQuick3DXrCamera::clipFar() const
267{
268 return m_clipFar;
269}
270
271void QQuick3DXrCamera::setClipNear(float clipNear)
272{
273 if (qFuzzyCompare(m_clipNear, clipNear))
274 return;
275
276 m_clipNear = clipNear;
277
278 syncCameraSettings();
279
280 emit clipNearChanged(m_clipNear);
281}
282
283void QQuick3DXrCamera::setClipFar(float clipFar)
284{
285 if (qFuzzyCompare(m_clipFar, clipFar))
286 return;
287
288 m_clipFar = clipFar;
289
290 syncCameraSettings();
291
292 emit clipFarChanged(m_clipFar);
293}
294
295void QQuick3DXrCamera::itemChange(ItemChange change, const ItemChangeData &data)
296{
297 // Sanity check (If we get a tracked item we'll do this there instead).
298 if (change == QQuick3DObject::ItemParentHasChanged) {
299 if (data.item != nullptr) {
300 if (QQuick3DXrOrigin *xrOrigin = qobject_cast<QQuick3DXrOrigin *>(data.item)) {
301 xrOrigin->setCamera(this);
302 } else {
303 qWarning() << "XrCamera must be a child of an XrOrigin!";
304 setParentItem(nullptr);
305 }
306 } else {
307 QQuick3DNode::itemChange(change, data);
308 }
309 }
310}
311
312void QQuick3DXrCamera::syncCameraSettings()
313{
314 QQuick3DXrOrigin *xrOrigin = qobject_cast<QQuick3DXrOrigin *>(parentItem());
315 if (xrOrigin && xrOrigin->camera() == this)
316 xrOrigin->syncCameraSettings();
317}
318
319QT_END_NAMESPACE