1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "q3dscene_p.h" |
5 | #include "q3dcamera_p.h" |
6 | #include "q3dlight_p.h" |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | /*! |
11 | * \class Q3DScene |
12 | * \inmodule QtGraphs |
13 | * \brief Q3DScene class provides description of the 3D scene being visualized. |
14 | * |
15 | * The 3D scene contains a single active camera and a single active light source. |
16 | * Visualized data is assumed to be at a fixed location. |
17 | * |
18 | * The 3D scene also keeps track of the viewport in which graph rendering is done, |
19 | * the primary subviewport inside the viewport where the main 3D graphs view resides |
20 | * and the secondary subviewport where the 2D sliced view of the data resides. The subviewports are |
21 | * by default resized by the \a Q3DScene. To override the resize behavior you need to listen to both |
22 | * \l viewportChanged() and \l slicingActiveChanged() signals and recalculate the subviewports accordingly. |
23 | * |
24 | * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. |
25 | * \note Not all graphs support the secondary 2D slicing view. |
26 | */ |
27 | |
28 | /*! |
29 | * \class Q3DSceneChangeBitField |
30 | * \internal |
31 | */ |
32 | |
33 | /*! |
34 | * \qmltype Scene3D |
35 | * \inqmlmodule QtGraphs |
36 | * \ingroup graphs_qml |
37 | * \instantiates Q3DScene |
38 | * \brief Scene3D type provides description of the 3D scene being visualized. |
39 | * |
40 | * The 3D scene contains a single active camera and a single active light source. |
41 | * Visualized data is assumed to be at a fixed location. |
42 | * |
43 | * The 3D scene also keeps track of the viewport in which graph rendering is done, |
44 | * the primary subviewport inside the viewport where the main 3D graphs view resides |
45 | * and the secondary subviewport where the 2D sliced view of the data resides. |
46 | * |
47 | * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. |
48 | * \note Not all graphs support the secondary 2D slicing view. |
49 | */ |
50 | |
51 | /*! |
52 | * \qmlproperty rect Scene3D::viewport |
53 | * |
54 | * The current viewport rectangle where all 3D rendering is targeted. |
55 | */ |
56 | |
57 | /*! |
58 | * \qmlproperty rect Scene3D::primarySubViewport |
59 | * |
60 | * The current subviewport rectangle inside the viewport where the |
61 | * primary view of the graphs is targeted. |
62 | * |
63 | * If slicingActive is \c false, the primary sub viewport will be equal to the |
64 | * viewport. If slicingActive is \c true and the primary sub viewport has not |
65 | * been explicitly set, it will be one fifth of the viewport. |
66 | * \note Setting primarySubViewport larger than or outside of viewport resizes viewport accordingly. |
67 | */ |
68 | |
69 | /*! |
70 | * \qmlproperty rect Scene3D::secondarySubViewport |
71 | * |
72 | * The secondary viewport rectangle inside the viewport. The secondary viewport |
73 | * is used for drawing the 2D slice view in some graphs. If it has not |
74 | * been explicitly set, it will be null. If slicingActive is \c true, it will |
75 | * be equal to the viewport. |
76 | * \note If the secondary sub viewport is larger than or outside of the |
77 | * viewport, the viewport is resized accordingly. |
78 | */ |
79 | |
80 | /*! |
81 | * \qmlproperty point Scene3D::selectionQueryPosition |
82 | * |
83 | * The coordinates for the user input that should be processed |
84 | * by the scene as a selection. If this property is set to a value other than |
85 | * invalidSelectionPoint, the |
86 | * graph tries to select a data item at the given point within the primary viewport. |
87 | * After the rendering pass, the property is returned to its default state of |
88 | * invalidSelectionPoint. |
89 | */ |
90 | |
91 | /*! |
92 | * \qmlproperty point Scene3D::graphPositionQuery |
93 | * |
94 | * The coordinates for the user input that should be processed by the scene as a |
95 | * graph position query. If this property is set to value other than |
96 | * invalidSelectionPoint, the graph tries to match a graph position to the given point |
97 | * within the primary viewport. |
98 | * After the rendering pass, this property is returned to its default state of |
99 | * invalidSelectionPoint. The queried graph position can be read from the |
100 | * AbstractGraph3D::queriedGraphPosition property after the next render pass. |
101 | * |
102 | * There is no single correct 3D coordinate to match a particular screen position, so to be |
103 | * consistent, the queries are always done against the inner sides of an invisible box surrounding |
104 | * the graph. |
105 | * |
106 | * \note Bar graphs allow graph position queries only at the graph floor level. |
107 | * |
108 | * \sa AbstractGraph3D::queriedGraphPosition |
109 | */ |
110 | |
111 | /*! |
112 | * \qmlproperty bool Scene3D::slicingActive |
113 | * |
114 | * Defines whether the 2D slicing view is currently active. If \c true, |
115 | * AbstractGraph3D::selectionMode must have either the |
116 | * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or |
117 | * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} |
118 | * set to a valid selection. |
119 | * \note Not all graphs support the 2D slicing view. |
120 | */ |
121 | |
122 | /*! |
123 | * \qmlproperty bool Scene3D::secondarySubviewOnTop |
124 | * |
125 | * Defines whether the 2D slicing view or the 3D view is drawn on top. |
126 | */ |
127 | |
128 | /*! |
129 | * \qmlproperty Camera3D Scene3D::activeCamera |
130 | * |
131 | * The currently active camera in the 3D scene. |
132 | * When a Camera3D is set in the property, it is automatically added as child of |
133 | * the scene. |
134 | */ |
135 | |
136 | /*! |
137 | * \qmlproperty Light3D Scene3D::activeLight |
138 | * |
139 | * The currently active light in the 3D scene. |
140 | * When a Light3D is set in the property, it is automatically added as child of |
141 | * the scene. |
142 | */ |
143 | |
144 | /*! |
145 | * \qmlproperty float Scene3D::devicePixelRatio |
146 | * |
147 | * The current device pixel ratio that is used when mapping input |
148 | * coordinates to pixel coordinates. |
149 | */ |
150 | |
151 | /*! |
152 | * \qmlproperty point Scene3D::invalidSelectionPoint |
153 | * A constant property providing an invalid point for selection. |
154 | */ |
155 | |
156 | /*! |
157 | * Constructs a basic scene with one light and one camera in it. An |
158 | * optional \a parent parameter can be given and is then passed to QObject constructor. |
159 | */ |
160 | Q3DScene::Q3DScene(QObject *parent) : |
161 | QObject(parent), |
162 | d_ptr(new Q3DScenePrivate(this)) |
163 | { |
164 | setActiveCamera(new Q3DCamera(0)); |
165 | setActiveLight(new Q3DLight(0)); |
166 | } |
167 | |
168 | /*! |
169 | * Destroys the 3D scene and all the objects contained within it. |
170 | */ |
171 | Q3DScene::~Q3DScene() |
172 | { |
173 | } |
174 | |
175 | /*! |
176 | * \property Q3DScene::viewport |
177 | * |
178 | * \brief A read only property that contains the current viewport rectangle |
179 | * where all the 3D rendering is targeted. |
180 | */ |
181 | QRect Q3DScene::viewport() const |
182 | { |
183 | const Q_D(Q3DScene); |
184 | return d->m_viewport; |
185 | } |
186 | |
187 | /*! |
188 | * \property Q3DScene::primarySubViewport |
189 | * |
190 | * \brief The current subviewport rectangle inside the viewport where the |
191 | * primary view of the graphs is targeted. |
192 | * |
193 | * If isSlicingActive() is \c false, the primary sub viewport is equal to |
194 | * viewport(). If isSlicingActive() is \c true and the primary sub viewport has |
195 | * not been explicitly set, it will be one fifth of viewport(). |
196 | * |
197 | * \note Setting primarySubViewport larger than or outside of the viewport |
198 | * resizes the viewport accordingly. |
199 | */ |
200 | QRect Q3DScene::primarySubViewport() const |
201 | { |
202 | const Q_D(Q3DScene); |
203 | QRect primary = d->m_primarySubViewport; |
204 | if (primary.isNull()) { |
205 | if (d->m_isSlicingActive) |
206 | primary = d->m_defaultSmallViewport; |
207 | else |
208 | primary = d->m_defaultLargeViewport; |
209 | } |
210 | return primary; |
211 | } |
212 | |
213 | void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) |
214 | { |
215 | Q_D(Q3DScene); |
216 | if (d->m_primarySubViewport != primarySubViewport) { |
217 | if (!primarySubViewport.isValid() && !primarySubViewport.isNull()) { |
218 | qWarning(msg: "Viewport is invalid." ); |
219 | return; |
220 | } |
221 | |
222 | // If viewport is smaller than primarySubViewport, enlarge it |
223 | if ((d->m_viewport.width() < (primarySubViewport.width() |
224 | + primarySubViewport.x())) |
225 | || (d->m_viewport.height() < (primarySubViewport.height() |
226 | + primarySubViewport.y()))) { |
227 | d->m_viewport.setWidth(qMax(a: d->m_viewport.width(), |
228 | b: primarySubViewport.width() + primarySubViewport.x())); |
229 | d->m_viewport.setHeight(qMax(a: d->m_viewport.height(), |
230 | b: primarySubViewport.height() + primarySubViewport.y())); |
231 | d->calculateSubViewports(); |
232 | } |
233 | |
234 | d->m_primarySubViewport = primarySubViewport; |
235 | d->updateGLSubViewports(); |
236 | d->m_changeTracker.primarySubViewportChanged = true; |
237 | d->m_sceneDirty = true; |
238 | |
239 | emit primarySubViewportChanged(subViewport: primarySubViewport); |
240 | emit d->needRender(); |
241 | } |
242 | } |
243 | |
244 | /*! |
245 | * Returns whether the given \a point resides inside the primary subview or not. |
246 | * \return \c true if the point is inside the primary subview. |
247 | * \note If subviews are superimposed, and the given \a point resides inside both, result is |
248 | * \c true only when the primary subview is on top. |
249 | */ |
250 | bool Q3DScene::isPointInPrimarySubView(const QPoint &point) |
251 | { |
252 | Q_D(Q3DScene); |
253 | int x = point.x(); |
254 | int y = point.y(); |
255 | bool isInSecondary = d->isInArea(area: secondarySubViewport(), x, y); |
256 | if (!isInSecondary || (isInSecondary && !d->m_isSecondarySubviewOnTop)) |
257 | return d->isInArea(area: primarySubViewport(), x, y); |
258 | else |
259 | return false; |
260 | } |
261 | |
262 | /*! |
263 | * Returns whether the given \a point resides inside the secondary subview or not. |
264 | * \return \c true if the point is inside the secondary subview. |
265 | * \note If subviews are superimposed, and the given \a point resides inside both, result is |
266 | * \c true only when the secondary subview is on top. |
267 | */ |
268 | bool Q3DScene::isPointInSecondarySubView(const QPoint &point) |
269 | { |
270 | Q_D(Q3DScene); |
271 | int x = point.x(); |
272 | int y = point.y(); |
273 | bool isInPrimary = d->isInArea(area: primarySubViewport(), x, y); |
274 | if (!isInPrimary || (isInPrimary && d->m_isSecondarySubviewOnTop)) |
275 | return d->isInArea(area: secondarySubViewport(), x, y); |
276 | else |
277 | return false; |
278 | } |
279 | |
280 | /*! |
281 | * \property Q3DScene::secondarySubViewport |
282 | * |
283 | * \brief The secondary viewport rectangle inside the viewport. |
284 | * |
285 | * The secondary viewport is used for drawing the 2D slice view in some |
286 | * graphs. If it has not been explicitly set, it will be equal to |
287 | * QRect. If isSlicingActive() is \c true, it will be equal to \l viewport. |
288 | * \note If the secondary sub viewport is larger than or outside of the |
289 | * viewport, the viewport is resized accordingly. |
290 | */ |
291 | QRect Q3DScene::secondarySubViewport() const |
292 | { |
293 | const Q_D(Q3DScene); |
294 | QRect secondary = d->m_secondarySubViewport; |
295 | if (secondary.isNull() && d->m_isSlicingActive) |
296 | secondary = d->m_defaultLargeViewport; |
297 | return secondary; |
298 | } |
299 | |
300 | void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) |
301 | { |
302 | Q_D(Q3DScene); |
303 | if (d->m_secondarySubViewport != secondarySubViewport) { |
304 | if (!secondarySubViewport.isValid() && !secondarySubViewport.isNull()) { |
305 | qWarning(msg: "Viewport is invalid." ); |
306 | return; |
307 | } |
308 | |
309 | // If viewport is smaller than secondarySubViewport, enlarge it |
310 | if ((d->m_viewport.width() < (secondarySubViewport.width() |
311 | + secondarySubViewport.x())) |
312 | || (d->m_viewport.height() < (secondarySubViewport.height() |
313 | + secondarySubViewport.y()))) { |
314 | d->m_viewport.setWidth(qMax(a: d->m_viewport.width(), |
315 | b: secondarySubViewport.width() |
316 | + secondarySubViewport.x())); |
317 | d->m_viewport.setHeight(qMax(a: d->m_viewport.height(), |
318 | b: secondarySubViewport.height() |
319 | + secondarySubViewport.y())); |
320 | d->calculateSubViewports(); |
321 | } |
322 | |
323 | d->m_secondarySubViewport = secondarySubViewport; |
324 | d->updateGLSubViewports(); |
325 | d->m_changeTracker.secondarySubViewportChanged = true; |
326 | d->m_sceneDirty = true; |
327 | |
328 | emit secondarySubViewportChanged(subViewport: secondarySubViewport); |
329 | emit d->needRender(); |
330 | } |
331 | } |
332 | |
333 | /*! |
334 | * \property Q3DScene::selectionQueryPosition |
335 | * |
336 | * \brief The coordinates for the user input that should be processed |
337 | * by the scene as a selection. |
338 | * |
339 | * If this property is set to a value other than invalidSelectionPoint(), the |
340 | * graph tries to select a data item, axis label, or a custom item at the |
341 | * specified coordinates within the primary viewport. |
342 | * After the rendering pass, the property is returned to its default state of |
343 | * invalidSelectionPoint(). |
344 | * |
345 | * \sa QAbstract3DGraph::selectedElement |
346 | */ |
347 | void Q3DScene::setSelectionQueryPosition(const QPoint &point) |
348 | { |
349 | Q_D(Q3DScene); |
350 | if (point != d->m_selectionQueryPosition) { |
351 | d->m_selectionQueryPosition = point; |
352 | d->m_changeTracker.selectionQueryPositionChanged = true; |
353 | d->m_sceneDirty = true; |
354 | |
355 | emit selectionQueryPositionChanged(position: point); |
356 | emit d->needRender(); |
357 | } |
358 | } |
359 | |
360 | QPoint Q3DScene::selectionQueryPosition() const |
361 | { |
362 | const Q_D(Q3DScene); |
363 | return d->m_selectionQueryPosition; |
364 | } |
365 | |
366 | /*! |
367 | * \return a QPoint signifying an invalid selection position. |
368 | */ |
369 | QPoint Q3DScene::invalidSelectionPoint() |
370 | { |
371 | static const QPoint invalidSelectionPos(-1, -1); |
372 | return invalidSelectionPos; |
373 | } |
374 | |
375 | /*! |
376 | * \property Q3DScene::graphPositionQuery |
377 | * |
378 | * \brief The coordinates for the user input that should be processed |
379 | * by the scene as a graph position query. |
380 | * |
381 | * If this property is set to a value other than invalidSelectionPoint(), the |
382 | * graph tries to match a graph position to the specified coordinates |
383 | * within the primary viewport. |
384 | * After the rendering pass, this property is returned to its default state of |
385 | * invalidSelectionPoint(). The queried graph position can be read from the |
386 | * QAbstract3DGraph::queriedGraphPosition property after the next render pass. |
387 | * |
388 | * There is no single correct 3D coordinate to match a particular screen position, so to be |
389 | * consistent, the queries are always done against the inner sides of an invisible box surrounding |
390 | * the graph. |
391 | * |
392 | * \note Bar graphs allow graph position queries only at the graph floor level. |
393 | * |
394 | * \sa QAbstract3DGraph::queriedGraphPosition |
395 | */ |
396 | void Q3DScene::setGraphPositionQuery(const QPoint &point) |
397 | { |
398 | Q_D(Q3DScene); |
399 | if (point != d->m_graphPositionQueryPosition) { |
400 | d->m_graphPositionQueryPosition = point; |
401 | d->m_changeTracker.graphPositionQueryPositionChanged = true; |
402 | d->m_sceneDirty = true; |
403 | |
404 | emit graphPositionQueryChanged(position: point); |
405 | emit d->needRender(); |
406 | } |
407 | } |
408 | |
409 | QPoint Q3DScene::graphPositionQuery() const |
410 | { |
411 | const Q_D(Q3DScene); |
412 | return d->m_graphPositionQueryPosition; |
413 | } |
414 | |
415 | /*! |
416 | * \property Q3DScene::slicingActive |
417 | * |
418 | * \brief Whether the 2D slicing view is currently active. |
419 | * |
420 | * If \c true, QAbstract3DGraph::selectionMode must have either |
421 | * QAbstract3DGraph::SelectionRow or QAbstract3DGraph::SelectionColumn set |
422 | * to a valid selection. |
423 | * \note Not all graphs support the 2D slicing view. |
424 | */ |
425 | bool Q3DScene::isSlicingActive() const |
426 | { |
427 | const Q_D(Q3DScene); |
428 | return d->m_isSlicingActive; |
429 | } |
430 | |
431 | void Q3DScene::setSlicingActive(bool isSlicing) |
432 | { |
433 | Q_D(Q3DScene); |
434 | if (d->m_isSlicingActive != isSlicing) { |
435 | d->m_isSlicingActive = isSlicing; |
436 | d->m_changeTracker.slicingActivatedChanged = true; |
437 | d->m_sceneDirty = true; |
438 | |
439 | // Set secondary subview behind primary to achieve default functionality (= clicking on |
440 | // primary disables slice) |
441 | setSecondarySubviewOnTop(!isSlicing); |
442 | |
443 | d->calculateSubViewports(); |
444 | emit slicingActiveChanged(isSlicingActive: isSlicing); |
445 | emit d->needRender(); |
446 | } |
447 | } |
448 | |
449 | /*! |
450 | * \property Q3DScene::secondarySubviewOnTop |
451 | * |
452 | * \brief Whether the 2D slicing view or the 3D view is drawn on top. |
453 | */ |
454 | bool Q3DScene::isSecondarySubviewOnTop() const |
455 | { |
456 | const Q_D(Q3DScene); |
457 | return d->m_isSecondarySubviewOnTop; |
458 | } |
459 | |
460 | void Q3DScene::setSecondarySubviewOnTop(bool isSecondaryOnTop) |
461 | { |
462 | Q_D(Q3DScene); |
463 | if (d->m_isSecondarySubviewOnTop != isSecondaryOnTop) { |
464 | d->m_isSecondarySubviewOnTop = isSecondaryOnTop; |
465 | d->m_changeTracker.subViewportOrderChanged = true; |
466 | d->m_sceneDirty = true; |
467 | |
468 | emit secondarySubviewOnTopChanged(isSecondaryOnTop); |
469 | emit d->needRender(); |
470 | } |
471 | } |
472 | |
473 | /*! |
474 | * \property Q3DScene::activeCamera |
475 | * |
476 | * \brief The currently active camera in the 3D scene. |
477 | * |
478 | * When a new Q3DCamera object is set, it is automatically added as child of |
479 | * the scene. |
480 | */ |
481 | Q3DCamera *Q3DScene::activeCamera() const |
482 | { |
483 | const Q_D(Q3DScene); |
484 | return d->m_camera; |
485 | } |
486 | |
487 | void Q3DScene::setActiveCamera(Q3DCamera *camera) |
488 | { |
489 | Q_ASSERT(camera); |
490 | Q_D(Q3DScene); |
491 | |
492 | // Add new camera as child of the scene |
493 | if (camera->parent() != this) |
494 | camera->setParent(this); |
495 | |
496 | if (camera != d->m_camera) { |
497 | if (d->m_camera) { |
498 | disconnect(sender: d->m_camera, signal: &Q3DCamera::xRotationChanged, receiver: d, |
499 | slot: &Q3DScenePrivate::needRender); |
500 | disconnect(sender: d->m_camera, signal: &Q3DCamera::yRotationChanged, receiver: d, |
501 | slot: &Q3DScenePrivate::needRender); |
502 | disconnect(sender: d->m_camera, signal: &Q3DCamera::zoomLevelChanged, receiver: d, |
503 | slot: &Q3DScenePrivate::needRender); |
504 | } |
505 | |
506 | d->m_camera = camera; |
507 | d->m_changeTracker.cameraChanged = true; |
508 | d->m_sceneDirty = true; |
509 | |
510 | |
511 | if (camera) { |
512 | connect(sender: camera, signal: &Q3DCamera::xRotationChanged, context: d, |
513 | slot: &Q3DScenePrivate::needRender); |
514 | connect(sender: camera, signal: &Q3DCamera::yRotationChanged, context: d, |
515 | slot: &Q3DScenePrivate::needRender); |
516 | connect(sender: camera, signal: &Q3DCamera::zoomLevelChanged, context: d, |
517 | slot: &Q3DScenePrivate::needRender); |
518 | } |
519 | |
520 | emit activeCameraChanged(camera); |
521 | emit d->needRender(); |
522 | } |
523 | } |
524 | |
525 | /*! |
526 | * \property Q3DScene::activeLight |
527 | * |
528 | * \brief The currently active light in the 3D scene. |
529 | * |
530 | * When a new Q3DLight objects is set, it is automatically added as child of |
531 | * the scene. |
532 | */ |
533 | Q3DLight *Q3DScene::activeLight() const |
534 | { |
535 | const Q_D(Q3DScene); |
536 | return d->m_light; |
537 | } |
538 | |
539 | void Q3DScene::setActiveLight(Q3DLight *light) |
540 | { |
541 | Q_ASSERT(light); |
542 | Q_D(Q3DScene); |
543 | |
544 | // Add new light as child of the scene |
545 | if (light->parent() != this) |
546 | light->setParent(this); |
547 | |
548 | if (light != d->m_light) { |
549 | d->m_light = light; |
550 | d->m_changeTracker.lightChanged = true; |
551 | d->m_sceneDirty = true; |
552 | |
553 | emit activeLightChanged(light); |
554 | emit d->needRender(); |
555 | } |
556 | } |
557 | |
558 | /*! |
559 | * \property Q3DScene::devicePixelRatio |
560 | * |
561 | * \brief The device pixel ratio that is used when mapping input |
562 | * coordinates to pixel coordinates. |
563 | */ |
564 | float Q3DScene::devicePixelRatio() const |
565 | { |
566 | const Q_D(Q3DScene); |
567 | return d->m_devicePixelRatio; |
568 | } |
569 | |
570 | void Q3DScene::setDevicePixelRatio(float pixelRatio) |
571 | { |
572 | Q_D(Q3DScene); |
573 | if (d->m_devicePixelRatio != pixelRatio) { |
574 | d->m_devicePixelRatio = pixelRatio; |
575 | d->m_changeTracker.devicePixelRatioChanged = true; |
576 | d->m_sceneDirty = true; |
577 | |
578 | emit devicePixelRatioChanged(pixelRatio); |
579 | d->updateGLViewport(); |
580 | emit d->needRender(); |
581 | } |
582 | } |
583 | |
584 | Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : |
585 | q_ptr(q), |
586 | m_isSecondarySubviewOnTop(true), |
587 | m_devicePixelRatio(1.f), |
588 | m_camera(), |
589 | m_light(), |
590 | m_isUnderSideCameraEnabled(false), |
591 | m_isSlicingActive(false), |
592 | m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()), |
593 | m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()), |
594 | m_windowSize(QSize(0, 0)), |
595 | m_sceneDirty(true) |
596 | { |
597 | } |
598 | |
599 | Q3DScenePrivate::~Q3DScenePrivate() |
600 | { |
601 | delete m_camera; |
602 | delete m_light; |
603 | } |
604 | |
605 | // Copies changed values from this scene to the other scene. If the other scene had same changes, |
606 | // those changes are discarded. |
607 | void Q3DScenePrivate::sync(Q3DScenePrivate &other) |
608 | { |
609 | Q_Q(Q3DScene); |
610 | if (m_changeTracker.windowSizeChanged) { |
611 | other.setWindowSize(windowSize()); |
612 | m_changeTracker.windowSizeChanged = false; |
613 | other.m_changeTracker.windowSizeChanged = false; |
614 | } |
615 | if (m_changeTracker.viewportChanged) { |
616 | other.setViewport(m_viewport); |
617 | m_changeTracker.viewportChanged = false; |
618 | other.m_changeTracker.viewportChanged = false; |
619 | } |
620 | if (m_changeTracker.subViewportOrderChanged) { |
621 | other.q_func()->setSecondarySubviewOnTop(q->isSecondarySubviewOnTop()); |
622 | m_changeTracker.subViewportOrderChanged = false; |
623 | other.m_changeTracker.subViewportOrderChanged = false; |
624 | } |
625 | if (m_changeTracker.primarySubViewportChanged) { |
626 | other.q_func()->setPrimarySubViewport(q->primarySubViewport()); |
627 | m_changeTracker.primarySubViewportChanged = false; |
628 | other.m_changeTracker.primarySubViewportChanged = false; |
629 | } |
630 | if (m_changeTracker.secondarySubViewportChanged) { |
631 | other.q_func()->setSecondarySubViewport(q->secondarySubViewport()); |
632 | m_changeTracker.secondarySubViewportChanged = false; |
633 | other.m_changeTracker.secondarySubViewportChanged = false; |
634 | } |
635 | if (m_changeTracker.selectionQueryPositionChanged) { |
636 | other.q_func()->setSelectionQueryPosition(q->selectionQueryPosition()); |
637 | m_changeTracker.selectionQueryPositionChanged = false; |
638 | other.m_changeTracker.selectionQueryPositionChanged = false; |
639 | } |
640 | if (m_changeTracker.graphPositionQueryPositionChanged) { |
641 | other.q_func()->setGraphPositionQuery(q->graphPositionQuery()); |
642 | m_changeTracker.graphPositionQueryPositionChanged = false; |
643 | other.m_changeTracker.graphPositionQueryPositionChanged = false; |
644 | } |
645 | if (m_changeTracker.cameraChanged) { |
646 | m_camera->setDirty(true); |
647 | m_changeTracker.cameraChanged = false; |
648 | other.m_changeTracker.cameraChanged = false; |
649 | } |
650 | m_camera->d_func()->sync(other&: *other.m_camera); |
651 | |
652 | if (m_changeTracker.lightChanged) { |
653 | m_light->setDirty(true); |
654 | m_changeTracker.lightChanged = false; |
655 | other.m_changeTracker.lightChanged = false; |
656 | } |
657 | m_light->d_func()->sync(other&: *other.m_light); |
658 | |
659 | if (m_changeTracker.slicingActivatedChanged) { |
660 | other.q_func()->setSlicingActive(q->isSlicingActive()); |
661 | m_changeTracker.slicingActivatedChanged = false; |
662 | other.m_changeTracker.slicingActivatedChanged = false; |
663 | } |
664 | |
665 | if (m_changeTracker.devicePixelRatioChanged) { |
666 | other.q_func()->setDevicePixelRatio(q->devicePixelRatio()); |
667 | m_changeTracker.devicePixelRatioChanged = false; |
668 | other.m_changeTracker.devicePixelRatioChanged = false; |
669 | } |
670 | |
671 | m_sceneDirty = false; |
672 | other.m_sceneDirty = false; |
673 | } |
674 | |
675 | void Q3DScenePrivate::setViewport(const QRect &viewport) |
676 | { |
677 | if (m_viewport != viewport && viewport.isValid()) { |
678 | m_viewport = viewport; |
679 | calculateSubViewports(); |
680 | emit needRender(); |
681 | } |
682 | } |
683 | |
684 | void Q3DScenePrivate::setViewportSize(int width, int height) |
685 | { |
686 | if (m_viewport.width() != width || m_viewport.height() != height) { |
687 | m_viewport.setWidth(width); |
688 | m_viewport.setHeight(height); |
689 | calculateSubViewports(); |
690 | emit needRender(); |
691 | } |
692 | } |
693 | |
694 | /*! |
695 | * \internal |
696 | * Sets the size of the window being rendered to. With widget based graphs, this |
697 | * is equal to the size of the QWindow and is same as the bounding rectangle. |
698 | * With declarative graphs this is equal to the size of the QQuickWindow and |
699 | * can be different from the bounding rectangle. |
700 | */ |
701 | void Q3DScenePrivate::setWindowSize(const QSize &size) |
702 | { |
703 | if (m_windowSize != size) { |
704 | m_windowSize = size; |
705 | updateGLViewport(); |
706 | m_changeTracker.windowSizeChanged = true; |
707 | emit needRender(); |
708 | } |
709 | } |
710 | |
711 | QSize Q3DScenePrivate::windowSize() const |
712 | { |
713 | return m_windowSize; |
714 | } |
715 | |
716 | void Q3DScenePrivate::calculateSubViewports() |
717 | { |
718 | // Calculates the default subviewport layout, used when slicing |
719 | const float smallerViewPortRatio = 0.2f; |
720 | m_defaultSmallViewport = QRect(0, 0, |
721 | m_viewport.width() * smallerViewPortRatio, |
722 | m_viewport.height() * smallerViewPortRatio); |
723 | m_defaultLargeViewport = QRect(0, 0, |
724 | m_viewport.width(), |
725 | m_viewport.height()); |
726 | |
727 | updateGLViewport(); |
728 | } |
729 | |
730 | void Q3DScenePrivate::updateGLViewport() |
731 | { |
732 | Q_Q(Q3DScene); |
733 | // Update GL viewport |
734 | m_glViewport.setX(m_viewport.x() * m_devicePixelRatio); |
735 | m_glViewport.setY((m_windowSize.height() - (m_viewport.y() + m_viewport.height())) |
736 | * m_devicePixelRatio); |
737 | m_glViewport.setWidth(m_viewport.width() * m_devicePixelRatio); |
738 | m_glViewport.setHeight(m_viewport.height() * m_devicePixelRatio); |
739 | |
740 | m_changeTracker.viewportChanged = true; |
741 | m_sceneDirty = true; |
742 | |
743 | // Do default subviewport changes first, then allow signal listeners to override. |
744 | updateGLSubViewports(); |
745 | emit q->viewportChanged(viewport: m_viewport); |
746 | } |
747 | |
748 | void Q3DScenePrivate::updateGLSubViewports() |
749 | { |
750 | if (m_isSlicingActive) { |
751 | QRect primary = m_primarySubViewport; |
752 | QRect secondary = m_secondarySubViewport; |
753 | if (primary.isNull()) |
754 | primary = m_defaultSmallViewport; |
755 | if (secondary.isNull()) |
756 | secondary = m_defaultLargeViewport; |
757 | |
758 | m_glPrimarySubViewport.setX((primary.x() + m_viewport.x()) * m_devicePixelRatio); |
759 | m_glPrimarySubViewport.setY((m_windowSize.height() |
760 | - (primary.y() + primary.height() + m_viewport.y())) |
761 | * m_devicePixelRatio); |
762 | m_glPrimarySubViewport.setWidth(primary.width() * m_devicePixelRatio); |
763 | m_glPrimarySubViewport.setHeight(primary.height() * m_devicePixelRatio); |
764 | |
765 | m_glSecondarySubViewport.setX((secondary.x() + m_viewport.x()) * m_devicePixelRatio); |
766 | m_glSecondarySubViewport.setY((m_windowSize.height() |
767 | - (secondary.y() + secondary.height() + m_viewport.y())) |
768 | * m_devicePixelRatio); |
769 | m_glSecondarySubViewport.setWidth(secondary.width() * m_devicePixelRatio); |
770 | m_glSecondarySubViewport.setHeight(secondary.height() * m_devicePixelRatio); |
771 | } else { |
772 | m_glPrimarySubViewport.setX(m_viewport.x() * m_devicePixelRatio); |
773 | m_glPrimarySubViewport.setY((m_windowSize.height() - (m_viewport.y() + m_viewport.height())) |
774 | * m_devicePixelRatio); |
775 | m_glPrimarySubViewport.setWidth(m_viewport.width() * m_devicePixelRatio); |
776 | m_glPrimarySubViewport.setHeight(m_viewport.height() * m_devicePixelRatio); |
777 | |
778 | m_glSecondarySubViewport = QRect(); |
779 | } |
780 | } |
781 | |
782 | QRect Q3DScenePrivate::glViewport() |
783 | { |
784 | return m_glViewport; |
785 | } |
786 | |
787 | QRect Q3DScenePrivate::glPrimarySubViewport() |
788 | { |
789 | return m_glPrimarySubViewport; |
790 | } |
791 | |
792 | QRect Q3DScenePrivate::glSecondarySubViewport() |
793 | { |
794 | return m_glSecondarySubViewport; |
795 | } |
796 | |
797 | /*! |
798 | * \internal |
799 | * Calculates and sets the light position relative to the currently active camera using the given |
800 | * parameters. |
801 | * The relative 3D offset to the current camera position is defined in \a relativePosition. |
802 | * Optional \a fixedRotation fixes the light rotation around the graph area to the |
803 | * given value in degrees. |
804 | * Optional \a distanceModifier modifies the distance of the light from the graph. |
805 | */ |
806 | void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relativePosition, |
807 | float fixedRotation, float distanceModifier) |
808 | { |
809 | m_light->setPosition(m_camera->d_func()->calculatePositionRelativeToCamera(relativePosition, |
810 | fixedRotation, |
811 | distanceModifier)); |
812 | } |
813 | |
814 | void Q3DScenePrivate::markDirty() |
815 | { |
816 | m_sceneDirty = true; |
817 | emit needRender(); |
818 | } |
819 | |
820 | bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const |
821 | { |
822 | int areaMinX = area.x(); |
823 | int areaMaxX = area.x() + area.width(); |
824 | int areaMinY = area.y(); |
825 | int areaMaxY = area.y() + area.height(); |
826 | return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY ); |
827 | } |
828 | |
829 | QT_END_NAMESPACE |
830 | |