summaryrefslogtreecommitdiffstats
path: root/src/imports/studio3d/q3dsstudio3ditem.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2018-01-29 12:08:00 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2018-02-01 12:51:23 +0000
commit0e04f0d4dad213e323d11f4814581c8afc2b7e9d (patch)
tree0be19e577f689e9d0bb23730728b24ad5a1e8728 /src/imports/studio3d/q3dsstudio3ditem.cpp
parentfa1cc116705dfef96afb17bef995f9000c2355be (diff)
Studio3D: add scenegraph node and renderer object
Not functional yet, although the implementation is mostly complete. The API is obviously not there, for now we'll just have a source property to allow testing the internals. Change-Id: I6b189aa30a0bc486edb041879e094641e0e51ffc Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/imports/studio3d/q3dsstudio3ditem.cpp')
-rw-r--r--src/imports/studio3d/q3dsstudio3ditem.cpp186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/imports/studio3d/q3dsstudio3ditem.cpp b/src/imports/studio3d/q3dsstudio3ditem.cpp
index 2f710a6..ff642ea 100644
--- a/src/imports/studio3d/q3dsstudio3ditem.cpp
+++ b/src/imports/studio3d/q3dsstudio3ditem.cpp
@@ -28,18 +28,204 @@
****************************************************************************/
#include "q3dsstudio3ditem_p.h"
+#include "q3dsstudio3drenderer_p.h"
+#include "q3dsstudio3dnode_p.h"
+#include <QSGNode>
+#include <QLoggingCategory>
+#include <QRunnable>
+#include <QQuickWindow>
+#include <QQuickRenderControl>
+#include <QOffscreenSurface>
+#include <QQmlFile>
+#include <QGuiApplication>
+#include <Q3DSEngine>
+#include <private/q3dsutils_p.h>
+#include <Qt3DCore/QEntity>
+#include <Qt3DRender/QRenderSurfaceSelector>
+#include <Qt3DRender/private/qrendersurfaceselector_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcStudio3D, "q3ds.studio3d")
+
Q3DSStudio3DItem::Q3DSStudio3DItem(QQuickItem *parent)
: QQuickItem(parent)
{
setFlag(QQuickItem::ItemHasContents, true);
setAcceptedMouseButtons(Qt::MouseButtonMask);
+ Q3DSUtils::setDialogsEnabled(false);
}
Q3DSStudio3DItem::~Q3DSStudio3DItem()
{
}
+QUrl Q3DSStudio3DItem::source() const
+{
+ return m_source;
+}
+
+void Q3DSStudio3DItem::setSource(const QUrl &newSource)
+{
+ if (m_source == newSource)
+ return;
+
+ m_source = newSource;
+ emit sourceChanged();
+
+ if (window()) // else defer to itemChange()
+ createEngine();
+}
+
+void Q3DSStudio3DItem::createEngine()
+{
+ // note: cannot have an engine without the source set
+ // (since once we assume m_engine!=nullptr, m_engine->renderAspect() must be valid as well)
+
+ if (!m_engine) {
+ qCDebug(lcStudio3D, "creating engine");
+ QQuickWindow *w = window();
+ Q_ASSERT(w);
+
+ m_engine = new Q3DSEngine;
+ // Rendering will be driven manually from the Quick render thread via the QRenderAspect.
+ // We create the render aspect ourselves on the Quick render thread.
+ m_engine->setFlags(Q3DSEngine::WithoutRenderAspect);
+
+ if (QWindow *rw = QQuickRenderControl::renderWindowFor(w)) {
+ // rw is the top-level window that is backed by a native window. Do
+ // not use that though since we must not clash with e.g. the widget
+ // backingstore compositor in the gui thread.
+ QOffscreenSurface *dummySurface = new QOffscreenSurface;
+ dummySurface->setParent(qGuiApp); // parent to something suitably long-living
+ dummySurface->setFormat(rw->format());
+ dummySurface->create();
+ m_engine->setSurface(dummySurface);
+ } else {
+ m_engine->setSurface(w);
+ }
+ }
+
+ const QString fn = QQmlFile::urlToLocalFileOrQrc(m_source);
+ qCDebug(lcStudio3D) << "source is now" << fn;
+ const QSize sz(width(), height());
+ if (!sz.isEmpty())
+ sendResize(sz);
+
+ m_engine->setSource(fn);
+
+ // cannot start() here, that must be deferred
+
+ update();
+}
+
+QSGNode *Q3DSStudio3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
+{
+ // this on the render thread; the engine lives on the gui thread and should
+ // be ready at this point - unless there's no source set yet
+
+ if (!m_engine) {
+ delete node;
+ return nullptr;
+ }
+
+ Q3DSStudio3DNode *n = static_cast<Q3DSStudio3DNode *>(node);
+ if (!n)
+ n = new Q3DSStudio3DNode;
+
+ n->setRect(boundingRect());
+
+ if (!m_renderer)
+ m_renderer = new Q3DSStudio3DRenderer(this, n, m_engine->aspectEngine());
+
+ return n;
+}
+
+// Let's do resource handling correctly, which means handling releaseResources
+// on the item and connecting to the sceneGraphInvalidated signal. Care must be
+// taken to support the case of moving the item from window to another as well.
+
+void Q3DSStudio3DItem::itemChange(QQuickItem::ItemChange change,
+ const QQuickItem::ItemChangeData &changeData)
+{
+ if (change == QQuickItem::ItemSceneChange) {
+ if (changeData.window) {
+ connect(changeData.window, &QQuickWindow::sceneGraphInvalidated, this, [this]() {
+ // render thread
+ qCDebug(lcStudio3D, "[R] sceneGraphInvalidated");
+ delete m_renderer;
+ m_renderer = nullptr;
+ QMetaObject::invokeMethod(this, "destroyEngine");
+ }, Qt::DirectConnection);
+
+ if (!m_source.isEmpty() && !m_engine)
+ createEngine();
+ }
+ }
+}
+
+void Q3DSStudio3DItem::startEngine()
+{
+ // set root entity
+ m_engine->start();
+}
+
+void Q3DSStudio3DItem::destroyEngine()
+{
+ if (m_engine) {
+ qCDebug(lcStudio3D, "destroying engine");
+ delete m_engine; // recreate on next window change - if we are still around, that is
+ m_engine = nullptr;
+ }
+}
+
+class Releaser : public QRunnable
+{
+public:
+ Releaser(Q3DSStudio3DRenderer *r) : m_renderer(r) { }
+ void run() override {
+ delete m_renderer;
+ }
+private:
+ Q3DSStudio3DRenderer *m_renderer;
+};
+
+void Q3DSStudio3DItem::releaseResources()
+{
+ qCDebug(lcStudio3D, "releaseResources");
+
+ // by the time the runnable runs we (the item) may already be gone; that's fine, just pass the renderer ref
+ if (window()) {
+ destroyEngine();
+ if (m_renderer) {
+ window()->scheduleRenderJob(new Releaser(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ // this may not be an application exit; if this is just a window change then allow continuing
+ // by eventually creating a new renderer object
+ m_renderer = nullptr;
+ }
+ }
+}
+
+void Q3DSStudio3DItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+
+ if (!newGeometry.isEmpty() && m_engine)
+ sendResize(newGeometry.size().toSize());
+}
+
+void Q3DSStudio3DItem::sendResize(const QSize &size)
+{
+ Q_ASSERT(m_engine);
+ m_engine->resize(size);
+
+ Qt3DCore::QEntity *rootEntity = m_engine->rootEntity();
+ Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootEntity);
+ qCDebug(lcStudio3D, "Setting external render target size on surface selector %p", surfaceSelector);
+ if (surfaceSelector) {
+ surfaceSelector->setExternalRenderTargetSize(size);
+ surfaceSelector->setSurfacePixelRatio(window()->effectiveDevicePixelRatio());
+ }
+}
+
QT_END_NAMESPACE