1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include <Qt3DCore/qpropertyupdatedchange.h> |
38 | #include <Qt3DCore/qpropertynodeaddedchange.h> |
39 | #include <Qt3DCore/qpropertynoderemovedchange.h> |
40 | #include <Qt3DCore/private/qscene_p.h> |
41 | #include <Qt3DQuickScene2D/qscene2d.h> |
42 | #include <Qt3DRender/qpicktriangleevent.h> |
43 | #include <Qt3DRender/qobjectpicker.h> |
44 | |
45 | #include <QtCore/qthread.h> |
46 | #include <QtCore/qatomic.h> |
47 | #include <QtGui/qevent.h> |
48 | #include <QtGui/QOpenGLFunctions> |
49 | |
50 | #include <private/qscene2d_p.h> |
51 | #include <private/scene2d_p.h> |
52 | #include <private/scene2dmanager_p.h> |
53 | #include <private/scene2devent_p.h> |
54 | #include <private/texture_p.h> |
55 | #include <private/nodemanagers_p.h> |
56 | #include <private/resourceaccessor_p.h> |
57 | #include <private/attachmentpack_p.h> |
58 | #include <private/qt3dquickscene2d_logging_p.h> |
59 | #include <private/qbackendnode_p.h> |
60 | #include <private/qobjectpicker_p.h> |
61 | #include <private/qpickevent_p.h> |
62 | #include <private/qpicktriangleevent_p.h> |
63 | #include <private/entity_p.h> |
64 | #include <private/platformsurfacefilter_p.h> |
65 | #include <private/trianglesvisitor_p.h> |
66 | |
67 | |
68 | QT_BEGIN_NAMESPACE |
69 | |
70 | #ifndef GL_DEPTH24_STENCIL8 |
71 | #define GL_DEPTH24_STENCIL8 0x88F0 |
72 | #endif |
73 | |
74 | using namespace Qt3DRender::Quick; |
75 | |
76 | namespace Qt3DRender { |
77 | |
78 | namespace Render { |
79 | |
80 | namespace Quick { |
81 | |
82 | Q_GLOBAL_STATIC(QThread, renderThread) |
83 | Q_GLOBAL_STATIC(QAtomicInt, renderThreadClientCount) |
84 | |
85 | RenderQmlEventHandler::RenderQmlEventHandler(Scene2D *node) |
86 | : QObject() |
87 | , m_node(node) |
88 | { |
89 | } |
90 | |
91 | // Event handler for the RenderQmlToTexture::renderThread |
92 | bool RenderQmlEventHandler::event(QEvent *e) |
93 | { |
94 | switch (static_cast<Scene2DEvent::Type>(e->type())) { |
95 | |
96 | case Scene2DEvent::Render: { |
97 | m_node->render(); |
98 | return true; |
99 | } |
100 | |
101 | case Scene2DEvent::Initialize: { |
102 | m_node->initializeRender(); |
103 | return true; |
104 | } |
105 | |
106 | case Scene2DEvent::Quit: { |
107 | m_node->cleanup(); |
108 | return true; |
109 | } |
110 | |
111 | default: |
112 | break; |
113 | } |
114 | return QObject::event(event: e); |
115 | } |
116 | |
117 | Scene2D::Scene2D() |
118 | : Qt3DRender::Render::BackendNode(Qt3DCore::QBackendNode::ReadWrite) |
119 | , m_context(nullptr) |
120 | , m_shareContext(nullptr) |
121 | , m_renderThread(nullptr) |
122 | , m_sharedObject(nullptr) |
123 | , m_fbo(0) |
124 | , m_rbo(0) |
125 | , m_initialized(false) |
126 | , m_renderInitialized(false) |
127 | , m_mouseEnabled(true) |
128 | , m_renderPolicy(Qt3DRender::Quick::QScene2D::Continuous) |
129 | { |
130 | |
131 | } |
132 | |
133 | Scene2D::~Scene2D() |
134 | { |
135 | for (auto connection: qAsConst(t&: m_connections)) |
136 | QObject::disconnect(connection); |
137 | m_connections.clear(); |
138 | } |
139 | |
140 | void Scene2D::setOutput(Qt3DCore::QNodeId outputId) |
141 | { |
142 | m_outputId = outputId; |
143 | } |
144 | |
145 | void Scene2D::initializeSharedObject() |
146 | { |
147 | if (!m_initialized) { |
148 | // bail out if we're running autotests |
149 | if (!qgetenv(varName: "QT3D_SCENE2D_DISABLE_RENDERING" ).isEmpty()) |
150 | return; |
151 | |
152 | renderThreadClientCount->fetchAndAddAcquire(valueToAdd: 1); |
153 | |
154 | renderThread->setObjectName(QStringLiteral("Scene2D::renderThread" )); |
155 | m_renderThread = renderThread; |
156 | m_sharedObject->m_renderThread = m_renderThread; |
157 | |
158 | // Create event handler for the render thread |
159 | m_sharedObject->m_renderObject = new RenderQmlEventHandler(this); |
160 | m_sharedObject->m_renderObject->moveToThread(thread: m_sharedObject->m_renderThread); |
161 | if (!m_sharedObject->m_renderThread->isRunning()) |
162 | m_sharedObject->m_renderThread->start(); |
163 | |
164 | // Notify main thread we have been initialized |
165 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderManager, |
166 | event: new Scene2DEvent(Scene2DEvent::Initialized)); |
167 | // Initialize render thread |
168 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderObject, |
169 | event: new Scene2DEvent(Scene2DEvent::Initialize)); |
170 | |
171 | m_initialized = true; |
172 | } |
173 | } |
174 | |
175 | void Scene2D::syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime) |
176 | { |
177 | Qt3DRender::Render::BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
178 | const QScene2D *node = qobject_cast<const QScene2D *>(object: frontEnd); |
179 | if (!node) |
180 | return; |
181 | const QScene2DPrivate *dnode = static_cast<const QScene2DPrivate *>(QScene2DPrivate::get(q: node)); |
182 | |
183 | if (m_mouseEnabled != node->isMouseEnabled()) { |
184 | m_mouseEnabled = node->isMouseEnabled(); |
185 | if (!firstTime && m_mouseEnabled && m_cachedPickEvent) { |
186 | handlePickEvent(type: QEvent::MouseButtonPress, ev: m_cachedPickEvent.data()); |
187 | m_cachedPickEvent.clear(); |
188 | } |
189 | } |
190 | |
191 | m_renderPolicy = node->renderPolicy(); |
192 | auto id = Qt3DCore::qIdForNode(node: node->output()); |
193 | if (id != m_outputId) |
194 | setOutput(id); |
195 | |
196 | auto ids = Qt3DCore::qIdsForNodes(nodes: node->entities()); |
197 | std::sort(first: std::begin(cont&: ids), last: std::end(cont&: ids)); |
198 | Qt3DCore::QNodeIdVector addedEntities; |
199 | Qt3DCore::QNodeIdVector removedEntities; |
200 | std::set_difference(first1: std::begin(cont&: ids), last1: std::end(cont&: ids), |
201 | first2: std::begin(cont&: m_entities), last2: std::end(cont&: m_entities), |
202 | result: std::inserter(x&: addedEntities, i: addedEntities.end())); |
203 | std::set_difference(first1: std::begin(cont&: m_entities), last1: std::end(cont&: m_entities), |
204 | first2: std::begin(cont&: ids), last2: std::end(cont&: ids), |
205 | result: std::inserter(x&: removedEntities, i: removedEntities.end())); |
206 | for (const auto &id: addedEntities) { |
207 | Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>(object: dnode->m_scene->lookupNode(id)); |
208 | if (!entity) |
209 | return; |
210 | |
211 | if (registerObjectPickerEvents(qentity: entity)) |
212 | m_entities.push_back(t: id); |
213 | else |
214 | Qt3DCore::QNodePrivate::get(q: const_cast<Qt3DCore::QNode *>(frontEnd))->update(); |
215 | } |
216 | for (const auto &id: removedEntities) { |
217 | m_entities.removeOne(t: id); |
218 | unregisterObjectPickerEvents(entityId: id); |
219 | } |
220 | std::sort(first: std::begin(cont&: m_entities), last: std::end(cont&: m_entities)); |
221 | |
222 | if (firstTime) |
223 | setSharedObject(dnode->m_renderManager->m_sharedObject); |
224 | } |
225 | |
226 | void Scene2D::setSharedObject(Qt3DRender::Quick::Scene2DSharedObjectPtr sharedObject) |
227 | { |
228 | m_sharedObject = sharedObject; |
229 | if (!m_initialized) |
230 | initializeSharedObject(); |
231 | } |
232 | |
233 | void Scene2D::initializeRender() |
234 | { |
235 | if (!m_renderInitialized && m_sharedObject.data() != nullptr) { |
236 | m_shareContext = renderer()->shareContext(); |
237 | if (!m_shareContext){ |
238 | qCDebug(Qt3DRender::Quick::Scene2D) << Q_FUNC_INFO << "Renderer not initialized." ; |
239 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderObject, |
240 | event: new Scene2DEvent(Scene2DEvent::Initialize)); |
241 | return; |
242 | } |
243 | m_context = new QOpenGLContext(); |
244 | m_context->setFormat(m_shareContext->format()); |
245 | m_context->setShareContext(m_shareContext); |
246 | m_context->create(); |
247 | |
248 | m_context->makeCurrent(surface: m_sharedObject->m_surface); |
249 | m_sharedObject->m_renderControl->initialize(gl: m_context); |
250 | #ifdef QT_OPENGL_ES_2_ANGLE |
251 | m_usingAngle = false; |
252 | if (m_context->isOpenGLES()) { |
253 | const char *versionStr = reinterpret_cast<const char *>( |
254 | m_context->functions()->glGetString(GL_VERSION)); |
255 | if (strstr(versionStr, "ANGLE" )) |
256 | m_usingAngle = true; |
257 | } |
258 | #endif |
259 | m_context->doneCurrent(); |
260 | |
261 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderManager, |
262 | event: new Scene2DEvent(Scene2DEvent::Prepare)); |
263 | m_renderInitialized = true; |
264 | } |
265 | } |
266 | |
267 | bool Scene2D::updateFbo(QOpenGLTexture *texture) |
268 | { |
269 | QOpenGLFunctions *gl = m_context->functions(); |
270 | if (m_fbo == 0) { |
271 | gl->glGenFramebuffers(n: 1, framebuffers: &m_fbo); |
272 | gl->glGenRenderbuffers(n: 1, renderbuffers: &m_rbo); |
273 | } |
274 | // TODO: Add another codepath when GL_DEPTH24_STENCIL8 is not supported |
275 | gl->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: m_rbo); |
276 | gl->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, |
277 | width: m_textureSize.width(), height: m_textureSize.height()); |
278 | gl->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: 0); |
279 | |
280 | gl->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_fbo); |
281 | gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
282 | GL_TEXTURE_2D, texture: texture->textureId(), level: 0); |
283 | gl->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer: m_rbo); |
284 | GLenum status = gl->glCheckFramebufferStatus(GL_FRAMEBUFFER); |
285 | gl->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: 0); |
286 | |
287 | if (status != GL_FRAMEBUFFER_COMPLETE) |
288 | return false; |
289 | return true; |
290 | } |
291 | |
292 | void Scene2D::syncRenderControl() |
293 | { |
294 | if (m_sharedObject->isSyncRequested()) { |
295 | |
296 | m_sharedObject->clearSyncRequest(); |
297 | |
298 | m_sharedObject->m_renderControl->sync(); |
299 | |
300 | // gui thread can now continue |
301 | m_sharedObject->wake(); |
302 | } |
303 | } |
304 | |
305 | void Scene2D::render() |
306 | { |
307 | if (m_initialized && m_renderInitialized && m_sharedObject.data() != nullptr) { |
308 | |
309 | QMutexLocker lock(&m_sharedObject->m_mutex); |
310 | |
311 | QOpenGLTexture *texture = nullptr; |
312 | const Qt3DRender::Render::Attachment *attachmentData = nullptr; |
313 | QMutex *textureLock = nullptr; |
314 | |
315 | #ifdef QT_OPENGL_ES_2_ANGLE |
316 | QScopedPointer<SurfaceLocker> surfaceLocker; |
317 | if (m_usingAngle) |
318 | surfaceLocker.reset(new SurfaceLocker(m_sharedObject->m_surface)); |
319 | #endif |
320 | m_context->makeCurrent(surface: m_sharedObject->m_surface); |
321 | |
322 | if (resourceAccessor()->accessResource(type: RenderBackendResourceAccessor::OutputAttachment, |
323 | nodeId: m_outputId, handle: (void**)&attachmentData, lock: nullptr)) { |
324 | if (!resourceAccessor()->accessResource(type: RenderBackendResourceAccessor::OGLTextureWrite, |
325 | nodeId: attachmentData->m_textureUuid, |
326 | handle: (void**)&texture, lock: &textureLock)) { |
327 | // Need to call sync even if the texture is not in use |
328 | syncRenderControl(); |
329 | m_context->doneCurrent(); |
330 | qCDebug(Qt3DRender::Quick::Scene2D) << Q_FUNC_INFO << "Texture not in use." ; |
331 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderObject, |
332 | event: new Scene2DEvent(Scene2DEvent::Render)); |
333 | return; |
334 | } |
335 | #ifdef QT_OPENGL_ES_2_ANGLE |
336 | if (m_usingAngle == false) |
337 | textureLock->lock(); |
338 | #else |
339 | textureLock->lock(); |
340 | #endif |
341 | const QSize textureSize = QSize(texture->width(), texture->height()); |
342 | if (m_attachmentData.m_textureUuid != attachmentData->m_textureUuid |
343 | || m_attachmentData.m_point != attachmentData->m_point |
344 | || m_attachmentData.m_face != attachmentData->m_face |
345 | || m_attachmentData.m_layer != attachmentData->m_layer |
346 | || m_attachmentData.m_mipLevel != attachmentData->m_mipLevel |
347 | || m_textureSize != textureSize) { |
348 | m_textureSize = textureSize; |
349 | m_attachmentData = *attachmentData; |
350 | if (!updateFbo(texture)) { |
351 | // Need to call sync even if the fbo is not usable |
352 | syncRenderControl(); |
353 | textureLock->unlock(); |
354 | m_context->doneCurrent(); |
355 | qCWarning(Qt3DRender::Quick::Scene2D) << Q_FUNC_INFO << "Fbo not initialized." ; |
356 | return; |
357 | } |
358 | } |
359 | } |
360 | |
361 | if (m_fbo != m_sharedObject->m_quickWindow->renderTargetId()) |
362 | m_sharedObject->m_quickWindow->setRenderTarget(fboId: m_fbo, size: m_textureSize); |
363 | |
364 | // Call disallow rendering while mutex is locked |
365 | if (m_renderPolicy == QScene2D::SingleShot) |
366 | m_sharedObject->disallowRender(); |
367 | |
368 | // Sync |
369 | if (m_sharedObject->isSyncRequested()) { |
370 | |
371 | m_sharedObject->clearSyncRequest(); |
372 | |
373 | m_sharedObject->m_renderControl->sync(); |
374 | } |
375 | |
376 | // Render |
377 | m_sharedObject->m_renderControl->render(); |
378 | |
379 | // Tell main thread we are done so it can begin cleanup if this is final frame |
380 | if (m_renderPolicy == QScene2D::SingleShot) |
381 | QCoreApplication::postEvent(receiver: m_sharedObject->m_renderManager, |
382 | event: new Scene2DEvent(Scene2DEvent::Rendered)); |
383 | |
384 | m_sharedObject->m_quickWindow->resetOpenGLState(); |
385 | m_context->functions()->glFlush(); |
386 | if (texture->isAutoMipMapGenerationEnabled()) |
387 | texture->generateMipMaps(); |
388 | #ifdef QT_OPENGL_ES_2_ANGLE |
389 | if (m_usingAngle == false) |
390 | textureLock->unlock(); |
391 | #else |
392 | textureLock->unlock(); |
393 | #endif |
394 | m_context->doneCurrent(); |
395 | |
396 | // gui thread can now continue |
397 | m_sharedObject->wake(); |
398 | } |
399 | } |
400 | |
401 | // this function gets called while the main thread is waiting |
402 | void Scene2D::cleanup() |
403 | { |
404 | if (m_renderInitialized && m_initialized) { |
405 | m_context->makeCurrent(surface: m_sharedObject->m_surface); |
406 | m_sharedObject->m_renderControl->invalidate(); |
407 | m_context->functions()->glDeleteFramebuffers(n: 1, framebuffers: &m_fbo); |
408 | m_context->functions()->glDeleteRenderbuffers(n: 1, renderbuffers: &m_rbo); |
409 | m_context->doneCurrent(); |
410 | m_renderInitialized = false; |
411 | } |
412 | if (m_initialized) { |
413 | delete m_sharedObject->m_renderObject; |
414 | m_sharedObject->m_renderObject = nullptr; |
415 | delete m_context; |
416 | m_context = nullptr; |
417 | m_initialized = false; |
418 | } |
419 | if (m_sharedObject) { |
420 | // wake up the main thread |
421 | m_sharedObject->wake(); |
422 | m_sharedObject = nullptr; |
423 | } |
424 | if (m_renderThread) { |
425 | renderThreadClientCount->fetchAndSubAcquire(valueToAdd: 1); |
426 | if (renderThreadClientCount->loadRelaxed() == 0) |
427 | renderThread->quit(); |
428 | } |
429 | } |
430 | |
431 | |
432 | bool Scene2D::registerObjectPickerEvents(Qt3DCore::QEntity *qentity) |
433 | { |
434 | Entity *entity = nullptr; |
435 | if (!resourceAccessor()->accessResource(type: RenderBackendResourceAccessor::EntityHandle, |
436 | nodeId: qentity->id(), handle: (void**)&entity, lock: nullptr)) { |
437 | qCWarning(Qt3DRender::Quick::Scene2D) << Q_FUNC_INFO |
438 | << "Entity not yet available in backend" ; |
439 | return false; |
440 | } |
441 | |
442 | if (!entity->containsComponentsOfType<ObjectPicker>() || |
443 | !entity->containsComponentsOfType<GeometryRenderer>()) { |
444 | qCWarning(Qt3DRender::Quick::Scene2D) << Q_FUNC_INFO |
445 | << "Entity does not contain required components: ObjectPicker and GeometryRenderer" ; |
446 | return false; |
447 | } |
448 | |
449 | QObjectPicker *picker = qentity->componentsOfType<QObjectPicker>().front(); |
450 | m_connections << QObject::connect(sender: picker, signal: &QObjectPicker::pressed, context: qentity, slot: [this](Qt3DRender::QPickEvent *pick) { |
451 | handlePickEvent(type: QEvent::MouseButtonPress, ev: pick); |
452 | }); |
453 | m_connections << QObject::connect(sender: picker, signal: &QObjectPicker::released, context: qentity, slot: [this](Qt3DRender::QPickEvent *pick) { |
454 | handlePickEvent(type: QEvent::MouseButtonRelease, ev: pick); |
455 | }); |
456 | m_connections << QObject::connect(sender: picker, signal: &QObjectPicker::moved, context: qentity, slot: [this](Qt3DRender::QPickEvent *pick) { |
457 | handlePickEvent(type: QEvent::MouseMove, ev: pick); |
458 | }); |
459 | |
460 | Qt3DCore::QBackendNodePrivate *priv = Qt3DCore::QBackendNodePrivate::get(n: this); |
461 | Qt3DCore::QChangeArbiter *arbiter = static_cast<Qt3DCore::QChangeArbiter*>(priv->m_arbiter); |
462 | arbiter->registerObserver(observer: d_ptr, nodeId: entity->componentUuid<ObjectPicker>()); |
463 | return true; |
464 | } |
465 | |
466 | void Scene2D::unregisterObjectPickerEvents(Qt3DCore::QNodeId entityId) |
467 | { |
468 | Entity *entity = nullptr; |
469 | if (!resourceAccessor()->accessResource(type: RenderBackendResourceAccessor::EntityHandle, |
470 | nodeId: entityId, handle: (void**)&entity, lock: nullptr)) |
471 | return; |
472 | |
473 | Qt3DCore::QBackendNodePrivate *priv = Qt3DCore::QBackendNodePrivate::get(n: this); |
474 | Qt3DCore::QChangeArbiter *arbiter = static_cast<Qt3DCore::QChangeArbiter*>(priv->m_arbiter); |
475 | arbiter->unregisterObserver(observer: d_ptr, nodeId: entity->componentUuid<ObjectPicker>()); |
476 | } |
477 | |
478 | void Scene2D::handlePickEvent(int type, const Qt3DRender::QPickEvent *ev) |
479 | { |
480 | if (!isEnabled()) |
481 | return; |
482 | if (m_mouseEnabled) { |
483 | const QPickTriangleEvent *pickTriangle = static_cast<const QPickTriangleEvent *>(ev); |
484 | Q_ASSERT(pickTriangle->entity()); |
485 | Entity *entity = nullptr; |
486 | if (!resourceAccessor()->accessResource(type: RenderBackendResourceAccessor::EntityHandle, |
487 | nodeId: Qt3DCore::qIdForNode(node: pickTriangle->entity()), |
488 | handle: (void**)&entity, lock: nullptr)) |
489 | return; |
490 | |
491 | CoordinateReader reader(renderer()->nodeManagers()); |
492 | if (reader.setGeometry(renderer: entity->renderComponent<GeometryRenderer>(), |
493 | attributeName: QAttribute::defaultTextureCoordinateAttributeName())) { |
494 | Vector4D c0 = reader.getCoordinate(vertexIndex: pickTriangle->vertex1Index()); |
495 | Vector4D c1 = reader.getCoordinate(vertexIndex: pickTriangle->vertex2Index()); |
496 | Vector4D c2 = reader.getCoordinate(vertexIndex: pickTriangle->vertex3Index()); |
497 | Vector4D ci = c0 * pickTriangle->uvw().x() |
498 | + c1 * pickTriangle->uvw().y() + c2 * pickTriangle->uvw().z(); |
499 | ci.setW(1.0f); |
500 | |
501 | const QSize size = m_sharedObject->m_quickWindow->size(); |
502 | QPointF pos = QPointF(ci.x() * size.width(), (1.0f - ci.y()) * size.height()); |
503 | QMouseEvent *mouseEvent |
504 | = new QMouseEvent(static_cast<QEvent::Type>(type), |
505 | pos, pos, pos, |
506 | static_cast<Qt::MouseButton>(pickTriangle->button()), |
507 | static_cast<Qt::MouseButtons>(pickTriangle->buttons()), |
508 | static_cast<Qt::KeyboardModifiers>(pickTriangle->modifiers()), |
509 | Qt::MouseEventSynthesizedByApplication); |
510 | |
511 | QCoreApplication::postEvent(receiver: m_sharedObject->m_quickWindow, event: mouseEvent); |
512 | } |
513 | } else if (type == QEvent::MouseButtonPress) { |
514 | const QPickTriangleEvent *pickTriangle = static_cast<const QPickTriangleEvent *>(ev); |
515 | const QPickTriangleEventPrivate *dpick = QPickTriangleEventPrivate::get(ev: pickTriangle); |
516 | m_cachedPickEvent = QPickEventPtr(dpick->clone()); |
517 | } else { |
518 | m_cachedPickEvent.clear(); |
519 | } |
520 | } |
521 | |
522 | } // namespace Quick |
523 | } // namespace Render |
524 | } // namespace Qt3DRender |
525 | |
526 | QT_END_NAMESPACE |
527 | |