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