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