diff options
Diffstat (limited to 'src/api/studio3dqml')
-rw-r--r-- | src/api/studio3dqml/q3dsplugin.cpp | 62 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dsplugin.h | 60 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dspresentationitem.cpp | 160 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dspresentationitem_p.h | 76 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dsrenderer.cpp | 458 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dsrenderer_p.h | 110 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dsstudio3d.cpp | 535 | ||||
-rw-r--r-- | src/api/studio3dqml/q3dsstudio3d_p.h | 137 | ||||
-rw-r--r-- | src/api/studio3dqml/qmldir | 3 | ||||
-rw-r--r-- | src/api/studio3dqml/studio3dqml.pro | 34 |
10 files changed, 1635 insertions, 0 deletions
diff --git a/src/api/studio3dqml/q3dsplugin.cpp b/src/api/studio3dqml/q3dsplugin.cpp new file mode 100644 index 0000000..640018f --- /dev/null +++ b/src/api/studio3dqml/q3dsplugin.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dsplugin.h" + +#include <QtQml/qqml.h> + +#include <QtStudio3D/private/q3dsviewersettings_p.h> + +#include "q3dsstudio3d_p.h" +#include "q3dspresentationitem_p.h" +#include "q3dsqmlstream.h" +#include "q3dsqmlsubpresentationsettings.h" +#include "q3dssceneelement.h" +#include "q3dsdatainput.h" + +QT_BEGIN_NAMESPACE + +void Q3DSPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == QLatin1String("QtStudio3D.OpenGL")); + + // @uri QtStudio3D.OpenGL + qmlRegisterType<Q3DSStudio3D>(uri, 2, 4, "Studio3D"); + qmlRegisterType<Q3DSViewerSettings>(uri, 2, 4, "ViewerSettings"); + qmlRegisterType<Q3DSPresentationItem>(uri, 2, 4, "Presentation"); + qmlRegisterType<Q3DSSceneElement>(uri, 2, 4, "SceneElement"); + qmlRegisterType<Q3DSElement>(uri, 2, 4, "Element"); + qmlRegisterType<Q3DSQmlStream>(uri, 2, 4, "QmlStream"); + qmlRegisterType<Q3DSSubPresentationSettings>(uri, 2, 4, "SubPresentationSettings"); + qmlRegisterType<Q3DSDataInput>(uri, 2, 4, "DataInput"); + qmlRegisterType<Q3DSDataOutput>(uri, 2, 4, "DataOutput"); +} + +QT_END_NAMESPACE diff --git a/src/api/studio3dqml/q3dsplugin.h b/src/api/studio3dqml/q3dsplugin.h new file mode 100644 index 0000000..04f6942 --- /dev/null +++ b/src/api/studio3dqml/q3dsplugin.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DS_PLUGIN_H +#define Q3DS_PLUGIN_H + +#include <QtQml/qqmlextensionplugin.h> + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtStudio3D_OpenGL); +#endif +} + +QT_BEGIN_NAMESPACE + +class Q3DSPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + Q3DSPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) + { + initResources(); + } + void registerTypes(const char *uri) override; +}; + +QT_END_NAMESPACE + +#endif // Q3DS_PLUGIN_H diff --git a/src/api/studio3dqml/q3dspresentationitem.cpp b/src/api/studio3dqml/q3dspresentationitem.cpp new file mode 100644 index 0000000..6372f2f --- /dev/null +++ b/src/api/studio3dqml/q3dspresentationitem.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dspresentationitem_p.h" + +#include <QtStudio3D/q3dssceneelement.h> +#include <QtStudio3D/q3dsdatainput.h> +#include <QtStudio3D/private/q3dspresentation_p.h> +#include <QtStudio3D/private/viewerqmlstreamproxy_p.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Presentation + \instantiates Q3DSPresentationItem + \inqmlmodule QtStudio3D + \ingroup OpenGLRuntime + \inherits Q3DSPresentation + \keyword Studio3D + + \brief Represents a Qt 3D Studio presentation. + + This item provides properties and methods for controlling a + presentation. + + Qt 3D Studio supports multiple presentations in one project. There + is always a main presentation and zero or more + sub-presentations. The sub-presentations are composed into the + main presentations either as contents of Qt 3D Studio layers or as + texture maps. + + In the filesystem each presentation corresponds to one \c{.uip} + presentation file. When present, the \c{.uia} project file ties + these together by specifying a name for each of the + (sub-)presentations and specifies which one is the main one. + + The \c{.uia} project also defines \l{DataInput}s and + \l{DataOutput}s that are exported by the presentations. + \l{DataInput}s provide a way to provide input to the presentation + to e.g. control a timeline of a subpresentation from code. + \l{DataOutput}s provide a way to get notified when an attribute + is changed in the presentation by animation timeline, + by behavior scripts or by a \l{DataInput}. + + The Presentation type handles child objects of the types \l Element, \l + SceneElement, \l DataInput, and \l SubPresentationSettings specially. These + will get automatically associated with the presentation and can control + certain aspects of it from that point on. + + From the API point of view Presentation corresponds to the + main presentation. The source property can refer either to a + \c{.uia} or \c{.uip} file. When specifying a file with \c{.uip} + extension and a \c{.uia} is present with the same name, the + \c{.uia} is loaded automatically and thus sub-presentation + information is available regardless. + */ +Q3DSPresentationItem::Q3DSPresentationItem(QObject *parent) + : Q3DSPresentation(parent) + , m_subPresentationSettings(nullptr) +{ +} + +Q3DSPresentationItem::~Q3DSPresentationItem() +{ +} + +/*! + Returns the \l SubPresentationSettings associated with this presentation. +*/ +Q3DSSubPresentationSettings *Q3DSPresentationItem::subPresentationSettings() const +{ + return m_subPresentationSettings; +} + +QQmlListProperty<QObject> Q3DSPresentationItem::qmlChildren() +{ + return QQmlListProperty<QObject>(this, nullptr, &appendQmlChildren, nullptr, nullptr, nullptr); +} + +void Q3DSPresentationItem::appendQmlChildren(QQmlListProperty<QObject> *list, QObject *obj) +{ + auto item = qobject_cast<Q3DSPresentationItem *>(list->object); + if (item) { + auto scene = qobject_cast<Q3DSSceneElement *>(obj); + if (scene) { + if (item->registeredElement(scene->elementPath())) + qWarning() << __FUNCTION__ << "A duplicate SceneElement defined for Presentation."; + else + item->registerElement(scene); + } else { + auto studioElement = qobject_cast<Q3DSElement *>(obj); + if (studioElement) { + if (item->registeredElement(studioElement->elementPath())) + qWarning() << __FUNCTION__ << "A duplicate Element defined for Presentation."; + else + item->registerElement(studioElement); + } else { + auto subPresSettings = qobject_cast<Q3DSSubPresentationSettings *>(obj); + if (subPresSettings) { + if (item->m_subPresentationSettings) { + qWarning() << __FUNCTION__ + << "Duplicate SubPresentationSettings defined for Presentation."; + } else { + item->m_subPresentationSettings = subPresSettings; + item->d_ptr->streamProxy()->setSettings(subPresSettings); + } + } else { + auto dataInput = qobject_cast<Q3DSDataInput *>(obj); + if (dataInput) { + if (item->registeredDataInput(dataInput->name())) { + qWarning() << __FUNCTION__ + << "Duplicate DataInput defined for Presentation."; + } else { + item->registerDataInput(dataInput); + } + } else { + auto dataOutput = qobject_cast<Q3DSDataOutput *>(obj); + if (dataOutput) { + if (item->registeredDataOutput(dataOutput->name())) { + qWarning() << __FUNCTION__ + << "Duplicate DataOutput defined for Presentation."; + } else { + item->registerDataOutput(dataOutput); + } + } + } + } + } + } + } +} + +QT_END_NAMESPACE diff --git a/src/api/studio3dqml/q3dspresentationitem_p.h b/src/api/studio3dqml/q3dspresentationitem_p.h new file mode 100644 index 0000000..216f73a --- /dev/null +++ b/src/api/studio3dqml/q3dspresentationitem_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DSPRESENTATIONITEM_H +#define Q3DSPRESENTATIONITEM_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtStudio3D API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "q3dsqmlsubpresentationsettings.h" + +#include <QtStudio3D/q3dspresentation.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +class Q3DSPresentationItem : public Q3DSPresentation +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<QObject> qmlChildren READ qmlChildren DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "qmlChildren") + +public: + explicit Q3DSPresentationItem(QObject *parent = nullptr); + ~Q3DSPresentationItem(); + + Q3DSSubPresentationSettings *subPresentationSettings() const; + + QQmlListProperty<QObject> qmlChildren(); + +public Q_SLOTS: + static void appendQmlChildren(QQmlListProperty<QObject> *list, QObject *obj); + +private: + Q3DSSubPresentationSettings *m_subPresentationSettings; + + friend class Q3DSStudio3D; +}; + +QT_END_NAMESPACE + +#endif // Q3DSPRESENTATIONITEM_H diff --git a/src/api/studio3dqml/q3dsrenderer.cpp b/src/api/studio3dqml/q3dsrenderer.cpp new file mode 100644 index 0000000..7366d6a --- /dev/null +++ b/src/api/studio3dqml/q3dsrenderer.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dsrenderer_p.h" +#include "Qt3DSViewerApp.h" +#include "Qt3DSAudioPlayerImpl.h" +#include "q3dspresentationitem_p.h" + +#include <QtStudio3D/private/q3dscommandqueue_p.h> +#include <QtStudio3D/private/q3dsviewersettings_p.h> +#include <QtStudio3D/private/q3dspresentation_p.h> +#include <QtStudio3D/private/studioutils_p.h> +#include <QtStudio3D/private/q3dsdatainput_p.h> + +#include <QtCore/qdebug.h> +#include <QtGui/qwindow.h> +#include <QtGui/qopenglcontext.h> +#include <QtQuick/qquickwindow.h> + +using namespace Q3DSViewer; + +QT_BEGIN_NAMESPACE + +Q3DSRenderer::Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor) + : m_visibleFlag(visibleFlag) + , m_initElements(false) + , m_runtime(0) + , m_window(nullptr) + , m_initialized(false) + , m_initializationFailure(false) + , m_visitor(assetVisitor) + , m_settings(new Q3DSViewerSettings(this)) + , m_presentation(new Q3DSPresentation(this)) +{ + m_startupTimer.start(); +} + +Q3DSRenderer::~Q3DSRenderer() +{ + releaseRuntime(); +} + +QOpenGLFramebufferObject *Q3DSRenderer::createFramebufferObject(const QSize &size) +{ + QOpenGLFramebufferObjectFormat theFormat; + theFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + QOpenGLFramebufferObject *frameBuffer = + new QOpenGLFramebufferObject(size, theFormat); + if (m_runtime && m_runtime->IsInitialised()) + m_runtime->setOffscreenId(frameBuffer->handle()); + return frameBuffer; +} + +/** Pull pending commands from the plugin. + * Invoked automatically by the QML scene graph. + * + * This is the only place where it is valid for the Q3DSStudio3D plugin and render to communicate. + */ +void Q3DSRenderer::synchronize(QQuickFramebufferObject *inView) +{ + // Passing m_InitElements here is a bit of a hack to easily set the flag on the plugin. + static_cast<Q3DSStudio3D *>(inView)->getCommands(m_initElements, m_commands); + + if (m_initializationFailure) + static_cast<Q3DSStudio3D *>(inView)->setError(m_error); + + if (m_commands.m_sourceChanged || m_commands.m_variantListChanged) { + releaseRuntime(); + // Need to update source and variant list here rather than + // processCommands, as both are needed for init + m_presentation->setVariantList(m_commands.m_variantList); + m_presentation->setSource(m_commands.m_source); + m_presentation->setDelayedLoading(m_commands.m_delayedLoading); + m_initialized = false; + m_initializationFailure = false; + m_error.clear(); + static_cast<Q3DSStudio3D *>(inView)->setError(QString()); + } + + m_initElements = false; + + // We need a handle to the window to be able to reset the GL state inside of Draw(). + // See https://bugreports.qt.io/browse/QTBUG-47213 + if (!m_window) + m_window = inView->window(); +} + +void Q3DSRenderer::releaseRuntime() +{ + m_settings->d_ptr->setViewerApp(nullptr); + m_presentation->d_ptr->setViewerApp(nullptr); + + if (m_runtime) { + m_runtime->Release(); + m_runtime = nullptr; + } +} + +/** Invoked by the QML scene graph indicating that it's time to render. + * Calls `draw()` if the plugin is visible, or `processCommands()` otherwise. + * + * Note that this will still render if the plugin is opacity:0. To avoid this, + * add a line to your QML to hide the object when opacity is zero, like: + * + * visible: opacity != 0 + */ +void Q3DSRenderer::render() +{ + // We may start in a non visible state but we still need + // to init the runtime otherwise the commands are never processed + if (!m_initialized && !m_initializationFailure) { + m_initialized = initializeRuntime(this->framebufferObject()); + m_initializationFailure = !m_initialized; + if (m_initializationFailure) + m_commands.clear(true); + } + + // Don't render if the plugin is hidden; however, if hidden, but sure + // to process pending commands so we can be shown again. + if (m_initialized) { + if (m_visibleFlag) + draw(); + else + processCommands(); + update(); // mark as dirty to ensure update again + } +} + +/** Cause Qt3DS runtime to render content. + * Initializes GL and the runtime when called the first time. + */ +void Q3DSRenderer::draw() +{ + if (m_runtime && m_runtime->IsInitialised() && m_window) { + if (m_initialized) + m_runtime->RestoreState(); + m_runtime->Render(); + m_runtime->SaveState(); + + m_window->resetOpenGLState(); + } +} + +bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo) +{ + m_runtime = &Q3DSViewerApp::Create(nullptr, new Qt3DSAudioPlayerImpl(), &m_startupTimer); + Q_ASSERT(m_runtime); + + // Connect presentation ready signal before initializing the app + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigPresentationReady, + this, &Q3DSRenderer::presentationReady); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigPresentationLoaded, + this, &Q3DSRenderer::presentationLoaded); + + int theWidth = inFbo->width(); + int theHeight = inFbo->height(); + + const QString localSource = Q3DSUtils::urlToLocalFileOrQrc(m_presentation->source()); + + if (!m_runtime->InitializeApp(theWidth, theHeight, + QOpenGLContext::currentContext()->format(), + inFbo->handle(), localSource, + m_presentation->variantList(), + m_presentation->delayedLoading(), + m_visitor)) { + m_error = m_runtime->error(); + releaseRuntime(); + return false; + } + + m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_INIT, + reinterpret_cast<qml_Function>(Q3DSRenderer::onInitHandler), + this); + m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_UPDATE, + reinterpret_cast<qml_Function>(Q3DSRenderer::onUpdateHandler), + this); + + m_settings->d_ptr->setViewerApp(m_runtime); + m_presentation->d_ptr->setViewerApp(m_runtime, false); + + // Connect signals + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered, + this, &Q3DSRenderer::enterSlide); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideExited, + this, &Q3DSRenderer::exitSlide); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, + this, &Q3DSRenderer::customSignalEmitted); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated, + this, &Q3DSRenderer::elementsCreated); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated, + this, &Q3DSRenderer::materialsCreated); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMeshesCreated, + this, &Q3DSRenderer::meshesCreated); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigDataOutputValueUpdated, + this, &Q3DSRenderer::dataOutputValueUpdated); + + return true; +} + +/** Accept user commands (e.g. setAttribute) needed to initialize the code. + * + * If we attempt to run Qt3DS methods like setAttribute() before the runtime + * has been initialized, they will be lost. This method is the correct place + * to accept user commands. + * + * Currently this method just sets a flag needed to pass a flag to the + * plugin next time syncronize() is called, which eventually gets the plugin + * to emit an `runningChanged` signal the next time `tick()` is called. + * As a result, code specified in an `onRunningChanged` handler may be run + * after one or more frames have already rendered. + */ +void Q3DSRenderer::onInitHandler(void *userData) +{ + Q3DSRenderer *theRenderer = static_cast<Q3DSRenderer *>(userData); + theRenderer->setInitElements(true); +} + +/** Accept the latest pending user commands (e.g. setAttribute). + * + * This method just calls `ProcessCommands` to avoid unnecessary + * pointer dereferencing and accessor methods (or public member variables). + */ +void Q3DSRenderer::onUpdateHandler(void *userData) +{ + Q3DSRenderer *theRenderer = static_cast<Q3DSRenderer *>(userData); + theRenderer->processCommands(); +} + +/** Apply commands queued up by the user (e.g. setAttribute). + * + * Note that these commands are executed even if the plugin is not visible, + * in part to allow changes to the visible flag to be noticed, but also + * to allow specialty code to continue to be queued up even when not rendering. + */ +void Q3DSRenderer::processCommands() +{ + if (!m_runtime) { + m_commands.clear(true); + return; + } + + if (m_commands.m_visibleChanged) + m_visibleFlag = m_commands.m_visible; + + if (QOpenGLFramebufferObject *inFbo = this->framebufferObject()) { + if (inFbo->isValid() && (inFbo->width() != m_runtime->GetWindowWidth() + || inFbo->height() != m_runtime->GetWindowHeight())) { + m_runtime->Resize(inFbo->width(), inFbo->height()); + } + } + + if (m_commands.m_scaleModeChanged) + m_settings->setScaleMode(m_commands.m_scaleMode); + if (m_commands.m_shadeModeChanged) + m_settings->setShadeMode(m_commands.m_shadeMode); + if (m_commands.m_matteColorChanged) + m_settings->setMatteColor(m_commands.m_matteColor); + if (m_commands.m_showRenderStatsChanged) + m_settings->setShowRenderStats(m_commands.m_showRenderStats); + if (m_commands.m_delayedLoadingChanged) + this->m_runtime->setDelayedLoading(m_commands.m_delayedLoading); + + if (m_commands.m_globalAnimationTimeChanged) + m_presentation->setGlobalAnimationTime(m_commands.m_globalAnimationTime); + + // Send scene graph changes over to Q3DS + for (int i = 0; i < m_commands.size(); i++) { + const ElementCommand &cmd = m_commands.constCommandAt(i); + switch (cmd.m_commandType) { + case CommandType_SetAttribute: + m_presentation->setAttribute(cmd.m_elementPath, cmd.m_stringValue, cmd.m_variantValue); + break; + case CommandType_SetPresentationActive: + m_presentation->setPresentationActive(cmd.m_elementPath, cmd.m_boolValue); + break; + case CommandType_GoToTime: + m_presentation->goToTime(cmd.m_elementPath, cmd.m_floatValue); + break; + case CommandType_GoToSlide: + m_presentation->goToSlide(cmd.m_elementPath, cmd.m_intValues[0]); + break; + case CommandType_GoToSlideByName: + m_presentation->goToSlide(cmd.m_elementPath, cmd.m_stringValue); + break; + case CommandType_GoToSlideRelative: + m_presentation->goToSlide(cmd.m_elementPath, bool(cmd.m_intValues[0]), + bool(cmd.m_intValues[1])); + break; + case CommandType_FireEvent: + m_presentation->fireEvent(cmd.m_elementPath, cmd.m_stringValue); + break; + case CommandType_MousePress: + m_runtime->HandleMousePress(cmd.m_intValues[0], + cmd.m_intValues[1], cmd.m_intValues[2], true); + break; + case CommandType_MouseRelease: + m_runtime->HandleMousePress(cmd.m_intValues[0], + cmd.m_intValues[1], cmd.m_intValues[2], false); + break; + case CommandType_MouseMove: + m_runtime->HandleMouseMove(cmd.m_intValues[0], cmd.m_intValues[1], true); + break; + case CommandType_MouseWheel: + m_runtime->HandleMouseWheel(cmd.m_intValues[0], cmd.m_intValues[1], + bool(cmd.m_intValues[2]), cmd.m_intValues[3]); + break; + case CommandType_KeyPress: + m_runtime->HandleKeyInput(Q3DStudio::EKeyCode(cmd.m_intValues[0]), true); + break; + case CommandType_KeyRelease: + m_runtime->HandleKeyInput(Q3DStudio::EKeyCode(cmd.m_intValues[0]), false); + break; + case CommandType_SetDataInputValue: + m_runtime->SetDataInputValue( + cmd.m_stringValue, cmd.m_variantValue, + static_cast<qt3ds::runtime::DataInputValueRole>(cmd.m_intValues[0])); + break; + case CommandType_CreateElements: { + m_runtime->createElements( + cmd.m_elementPath, cmd.m_stringValue, + *static_cast<QVector<QHash<QString, QVariant>> *>(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast<QVector<QHash<QString, QVariant>> *>(command.m_data); + command.m_data = nullptr; + break; + } + case CommandType_DeleteElements: { + m_runtime->deleteElements(*static_cast<QStringList *>(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast<QStringList *>(command.m_data); + command.m_data = nullptr; + break; + } + case CommandType_CreateMaterials: { + m_runtime->createMaterials(cmd.m_elementPath, *static_cast<QStringList *>(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast<QStringList *>(command.m_data); + command.m_data = nullptr; + break; + } + case CommandType_DeleteMaterials: { + m_runtime->deleteMaterials(*static_cast<QStringList *>(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast<QStringList *>(command.m_data); + command.m_data = nullptr; + break; + } + case CommandType_CreateMeshes: { + m_runtime->createMeshes(*static_cast<QHash<QString, Q3DSViewer::MeshData> *>( + cmd.m_data)); + // Runtime makes copy of the data, so we can delete it now + auto &command = m_commands.commandAt(i); + auto meshData = reinterpret_cast<QHash<QString, Q3DSViewer::MeshData> *>( + command.m_data); + delete meshData; + command.m_data = nullptr; + break; + } + case CommandType_DeleteMeshes: { + m_runtime->deleteMeshes(*static_cast<QStringList *>(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast<QStringList *>(command.m_data); + command.m_data = nullptr; + break; + } + case CommandType_RequestSlideInfo: { + int current = 0; + int previous = 0; + QString currentName; + QString previousName; + const QByteArray path(cmd.m_elementPath.toUtf8()); + m_runtime->GetSlideInfo(path, current, previous, currentName, previousName); + QVariantList *requestData = new QVariantList(); + requestData->append(QVariant(current)); + requestData->append(QVariant(previous)); + requestData->append(QVariant(currentName)); + requestData->append(QVariant(previousName)); + + Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData); + + break; + } + case CommandType_RequestDataInputs: { + QVariantList *requestData = new QVariantList(); + if (m_runtime) { + const auto diList = m_runtime->dataInputs(); + + for (const auto &it : diList) { + Q3DSDataInput *newIt = new Q3DSDataInput(it, nullptr); + newIt->d_ptr->m_max = m_runtime->dataInputMax(it); + newIt->d_ptr->m_min = m_runtime->dataInputMin(it); + newIt->d_ptr->m_metadata = m_runtime->dataInputMetadata(it); + requestData->append(QVariant::fromValue(newIt)); + } + } + + Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData); + break; + } + case CommandType_RequestDataOutputs: { + QVariantList *requestData = new QVariantList(); + if (m_presentation) { + const auto diList = m_presentation->dataOutputs(); + + for (const auto &it : diList) + requestData->append(QVariant::fromValue(it->name())); + } + + Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData); + break; + } + case CommandType_PreloadSlide: + m_runtime->preloadSlide(cmd.m_elementPath); + break; + case CommandType_UnloadSlide: + m_runtime->unloadSlide(cmd.m_elementPath); + break; + default: + qWarning() << __FUNCTION__ << "Unrecognized CommandType in command list!"; + } + } + + m_commands.clear(false); +} + +QT_END_NAMESPACE diff --git a/src/api/studio3dqml/q3dsrenderer_p.h b/src/api/studio3dqml/q3dsrenderer_p.h new file mode 100644 index 0000000..6d044ff --- /dev/null +++ b/src/api/studio3dqml/q3dsrenderer_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DS_RENDERER_H +#define Q3DS_RENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtStudio3D API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qopenglframebufferobject.h> +#include <QtQuick/qquickframebufferobject.h> + +#include "Qt3DSViewerApp.h" +#include "q3dsstudio3d_p.h" + +QT_BEGIN_NAMESPACE + +class Q3DSViewerSettings; +class Q3DSPresentation; + +class Q3DSRenderer : public QObject, + public QQuickFramebufferObject::Renderer +{ + Q_OBJECT + +public: + Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor); + ~Q3DSRenderer(); + + QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override; + + void setInitElements(bool inFlag) { m_initElements = inFlag; } + void processCommands(); + +Q_SIGNALS: + void enterSlide(const QString &elementPath, unsigned int slide, const QString &slideName); + void exitSlide(const QString &elementPath, unsigned int slide, const QString &slideName); + void requestResponse(const QString &elementPath, CommandType commandType, void *requestData); + void presentationReady(); + void presentationLoaded(); + void customSignalEmitted(const QString &elNmentPath, const QString &name); + void elementsCreated(const QStringList &elementPaths, const QString &error); + void materialsCreated(const QStringList &materialNames, const QString &error); + void meshesCreated(const QStringList &meshNames, const QString &error); + void dataOutputValueUpdated(const QString &name, const QVariant &newValue); + +protected: + static void onInitHandler(void *userData); + static void onUpdateHandler(void *userData); + bool initializeRuntime(QOpenGLFramebufferObject *inFbo); + void draw(); + void render() override; + void synchronize(QQuickFramebufferObject *inView) override; + void releaseRuntime(); + +protected: + bool m_visibleFlag; // Is the plugin visible? Prevents rendering hidden content. + CommandQueue m_commands; // A list of commands retrieved by the plugin to be applied. + bool m_initElements; // Flag set when the runtime is first ready to render. + Q3DSViewer::Q3DSViewerApp *m_runtime; // The Qt3DS viewer that renders all content. + QQuickWindow *m_window; // Window associated with the plugin; needed to reset OpenGL state. + + bool m_initialized; // Has the runtime and OpenGL state been initialized? + bool m_initializationFailure; // Initialization failed, no point in trying to init again + qt3ds::Qt3DSAssetVisitor *m_visitor; + + Q3DSViewerSettings *m_settings; + Q3DSPresentation *m_presentation; + QString m_error; + QElapsedTimer m_startupTimer; +}; + +QT_END_NAMESPACE + +#endif // Q3DS_RENDERER_H diff --git a/src/api/studio3dqml/q3dsstudio3d.cpp b/src/api/studio3dqml/q3dsstudio3d.cpp new file mode 100644 index 0000000..5a46089 --- /dev/null +++ b/src/api/studio3dqml/q3dsstudio3d.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dsstudio3d_p.h" +#include "q3dsrenderer_p.h" +#include "q3dspresentationitem_p.h" + +#include <QtStudio3D/private/q3dsviewersettings_p.h> +#include <QtStudio3D/private/q3dspresentation_p.h> +#include <QtStudio3D/private/q3dssceneelement_p.h> +#include <QtStudio3D/private/viewerqmlstreamproxy_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qfileinfo.h> +#include <QtQuick/qquickwindow.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Studio3D + \instantiates Q3DSStudio3D + \inqmlmodule QtStudio3D + \ingroup OpenGLRuntime + \inherits Item + \keyword Studio3D + + \brief Qt 3D Studio presentation viewer. + + This type enables developers to embed Qt 3D Studio presentations in Qt + Quick. + + \section2 Example Usage + + \qml + Studio3D { + id: studio3D + anchors.fill: parent + + Presentation { + source: "qrc:///presentation.uia" + SceneElement { + id: scene + elementPath: "Scene" + currentSlideIndex: 2 + } + Element { + id: textLabel + elementPath: "Scene.Layer.myLabel" + } + } + ViewerSettings { + showRenderStats: true + } + onRunningChanged: { + console.log("Presentation ready!"); + } + } + \endqml + + \section2 Controlling the presentation + + Like the example above suggests, Studio3D and the other types under the + QtStudio3D import offer more than simply rendering the animated Qt 3D + Studio presentation. They also offer scene manipulation, including + + \list + + \li querying and changing scene object properties (for example, the + transform of a model, colors and other settings of a material, etc.) via + Presentation::getAttribute(), Presentation::setAttribute(), \l Element, and + \l DataInput, + + \li changing slides (and thus starting the relevant animations and applying + the scene object property changes associated with the new slide) via + Presentation::goToSlide(), \l SceneElement, and \l DataInput, + + \li and controlling the timeline (the current playback position for the + key-frame based animations) both on the main scene and on individual + Component nodes via Presentation::goToTime(), \l SceneElement, and \l DataInput. + + \endlist + + \sa Presentation +*/ + +/*! + \qmlsignal Studio3D::frameUpdate() + + This signal is emitted each time a frame has been rendered. +*/ + +/*! + \qmlsignal Studio3D::presentationLoaded() + + This signal is emitted when the presentation has been loaded and is ready + to be shown. +*/ + +/*! + \qmlsignal Studio3D::presentationReady() + + This signal is emitted when the presentation has fully initialized its 3D + scene for the first frame. + + The difference to presentationLoaded() is that this signal is emitted only + when the asynchronous operations needed to build to 3D scene and the first + frame have completed. + + When implementing splash screens via Loader items and the Item::visible + property, this is the signal that should be used to trigger hiding the + splash screen. +*/ + +Q3DSStudio3D::Q3DSStudio3D() + : m_viewerSettings(nullptr) + , m_presentation(nullptr) + , m_emitRunningChange(false) + , m_isRunning(false) + , m_eventIgnoreFlags(EnableAllEvents) + , m_pixelRatio(1.0) +{ + setMirrorVertically(true); + connect(this, &Q3DSStudio3D::windowChanged, this, &Q3DSStudio3D::handleWindowChanged); + connect(this, &Q3DSStudio3D::visibleChanged, this, &Q3DSStudio3D::handleVisibleChanged); + + updateEventMasks(); +} + +Q3DSStudio3D::~Q3DSStudio3D() +{ +} + +/*! + \qmlproperty Presentation Studio3D::presentation + + Accessor for the presentation. Applications are expected to create a single + Presentation child object for Studio3D. If this is omitted, a presentation + is created automatically. + + This property is read-only. +*/ +Q3DSPresentationItem *Q3DSStudio3D::presentation() const +{ + return m_presentation; +} + +// #TODO: QT3DS-3566 viewerSettings is missing documentation +/*! + \qmlproperty ViewerSettings Studio3D::viewerSettings + + This property is read-only. +*/ +Q3DSViewerSettings *Q3DSStudio3D::viewerSettings() const +{ + return m_viewerSettings; +} + +/*! + \qmlproperty string Studio3D::error + + Contains the text for the error message that was generated during the + loading of the presentation. When no error occurred or there is no + presentation loaded, the value is an empty string. + + This property is read-only. +*/ +QString Q3DSStudio3D::error() const +{ + return m_error; +} + +void Q3DSStudio3D::setError(const QString &error) +{ + if (error != m_error) { + m_error = error; + Q_EMIT errorChanged(m_error); + } +} + +/*! + \qmlproperty EventIgnoreFlags Studio3D::ignoredEvents + + This property can be used to ignore mouse/wheel/keyboard events. + By default all events are enabled. +*/ + +Q3DSStudio3D::EventIgnoreFlags Q3DSStudio3D::ignoredEvents() const +{ + return m_eventIgnoreFlags; +} + +void Q3DSStudio3D::setIgnoredEvents(EventIgnoreFlags flags) +{ + if (m_eventIgnoreFlags == flags) + return; + + m_eventIgnoreFlags = flags; + updateEventMasks(); + Q_EMIT ignoredEventsChanged(); +} + +/*! + \internal + */ +void Q3DSStudio3D::updateEventMasks() +{ + if (m_eventIgnoreFlags.testFlag(IgnoreMouseEvents)) { + setAcceptedMouseButtons(Qt::NoButton); + setAcceptHoverEvents(false); + } else { + setAcceptedMouseButtons(Qt::MouseButtonMask); + setAcceptHoverEvents(true); + } +} + +/*! + \internal + */ +void Q3DSStudio3D::componentComplete() +{ + const auto childObjs = children(); + for (QObject *child : childObjs) { + auto settings = qobject_cast<Q3DSViewerSettings *>(child); + if (settings) { + if (m_viewerSettings) + qWarning("Duplicate ViewerSettings defined for Studio3D."); + else + m_viewerSettings = settings; + } + auto presentation = qobject_cast<Q3DSPresentationItem *>(child); + if (presentation) { + if (m_presentation) + qWarning("Duplicate Presentation defined for Studio3D."); + else + m_presentation = presentation; + } + } + if (!m_viewerSettings) + m_viewerSettings = new Q3DSViewerSettings(this); + if (!m_presentation) + m_presentation = new Q3DSPresentationItem(this); + + m_viewerSettings->d_ptr->setCommandQueue(&m_pendingCommands); + m_presentation->d_ptr->setCommandQueue(&m_pendingCommands); + + // Ensure qml stream proxy gets created on main thread + m_presentation->d_ptr->streamProxy(); + + QQuickFramebufferObject::componentComplete(); +} + +/*! + \internal + */ +void Q3DSStudio3D::handleWindowChanged(QQuickWindow *window) +{ + if (!window) + return; + + window->setClearBeforeRendering(false); + m_pixelRatio = window->devicePixelRatio(); + + // Call tick every frame of the GUI thread to notify QML about new frame via frameUpdate signal + connect(window, &QQuickWindow::afterAnimating, this, &Q3DSStudio3D::tick); + // Call update after the frame is handled to queue another frame + connect(window, &QQuickWindow::afterSynchronizing, this, &Q3DSStudio3D::update); +} + +/*! + \internal + Queue up a command to inform the renderer of a newly-changed visible/hidden status. + */ +void Q3DSStudio3D::handleVisibleChanged() +{ + m_pendingCommands.m_visibleChanged = true; + m_pendingCommands.m_visible = isVisible(); +} + +/*! + \brief internal + */ +void Q3DSStudio3D::reset() +{ + // Fake a source change to trigger a reloading of the presentation + m_pendingCommands.m_sourceChanged = true; + m_pendingCommands.m_source = m_presentation->source(); + m_pendingCommands.m_variantListChanged = true; + m_pendingCommands.m_variantList = m_presentation->variantList(); +} + +/*! + \internal + */ +void Q3DSStudio3D::requestResponseHandler(const QString &elementPath, CommandType commandType, + void *requestData) +{ + switch (commandType) { + case CommandType_RequestSlideInfo: { + Q3DSSceneElement *handler = qobject_cast<Q3DSSceneElement *>( + m_presentation->registeredElement(elementPath)); + if (handler) + handler->d_ptr->requestResponseHandler(commandType, requestData); + else + qWarning() << __FUNCTION__ << "RequestSlideInfo response got for unregistered scene."; + break; + } + case CommandType_RequestDataInputs: { + Q3DSPresentation *handler = qobject_cast<Q3DSPresentation *>(m_presentation); + if (handler) { + handler->d_ptr->requestResponseHandler(commandType, requestData); + } else { + qWarning() << __FUNCTION__ + << "RequestDataInputs response got for invalid presentation."; + } + break; + } + case CommandType_RequestDataOutputs: { + Q3DSPresentation *handler = qobject_cast<Q3DSPresentation *>(m_presentation); + if (handler) { + handler->d_ptr->requestResponseHandler(commandType, requestData); + } else { + qWarning() << __FUNCTION__ + << "RequestDataOutputs response got for invalid presentation."; + } + break; + } + default: + qWarning() << __FUNCTION__ << "Unknown command type."; + break; + } +} + +/*! + \internal + Create the Q3DSRenderer. Invoked automatically by the QML scene graph. + */ +QQuickFramebufferObject::Renderer *Q3DSStudio3D::createRenderer() const +{ + // It is "illegal" to create a connection between the renderer + // and the plugin, and vice-versa. The only valid time the two + // may communicate is during Q3DSRenderer::synchronize(). + Q3DSRenderer *renderer = new Q3DSRenderer(isVisible(), m_presentation->d_ptr->streamProxy()); + + connect(renderer, &Q3DSRenderer::enterSlide, + m_presentation->d_ptr, &Q3DSPresentationPrivate::handleSlideEntered); + connect(renderer, &Q3DSRenderer::dataOutputValueUpdated, + m_presentation->d_ptr, &Q3DSPresentationPrivate::handleDataOutputValueUpdate); + connect(renderer, &Q3DSRenderer::elementsCreated, + m_presentation->d_ptr, &Q3DSPresentationPrivate::handleElementsCreated); + connect(renderer, &Q3DSRenderer::materialsCreated, + m_presentation->d_ptr, &Q3DSPresentationPrivate::handleMaterialsCreated); + connect(renderer, &Q3DSRenderer::meshesCreated, + m_presentation->d_ptr, &Q3DSPresentationPrivate::handleMeshesCreated); + connect(renderer, &Q3DSRenderer::exitSlide, + m_presentation, &Q3DSPresentation::slideExited); + connect(renderer, &Q3DSRenderer::customSignalEmitted, + m_presentation, &Q3DSPresentation::customSignalEmitted); + connect(renderer, &Q3DSRenderer::requestResponse, + this, &Q3DSStudio3D::requestResponseHandler); + connect(renderer, &Q3DSRenderer::presentationLoaded, + this, &Q3DSStudio3D::presentationLoaded); + connect(renderer, &Q3DSRenderer::presentationReady, + this, &Q3DSStudio3D::presentationReady); + return renderer; +} + +/*! + \qmlproperty bool Studio3D::running + + The value of this property is \c true when the presentation has been loaded + and is ready to be shown. + + This property is read-only. +*/ +bool Q3DSStudio3D::isRunning() const +{ + return m_isRunning; +} + +/*! + \internal + Emit QML `runningChanged` and `frameUpdate` and signals. + This method is called every frame, and emits the `frameUpdate` signal every frame, + regardless of plugin visibility. This allows a hidden Qt3DSView to still process + information every frame, even though the Renderer is not rendering. + + To prevent expensive onFrameUpdate handlers from being processed when hidden, + add an early return to the top like: + + onFrameUpdate: { + if (!visible) return; + ... + } + */ +void Q3DSStudio3D::tick() +{ + if (m_emitRunningChange) { + m_isRunning = true; + Q_EMIT runningChanged(true); + m_emitRunningChange = false; + } + + // Don't call onFrameUpdate until after onInitialize has been called + if (m_isRunning) { + // Give QML an opportunity to change Qt3DS values every frame + Q_EMIT frameUpdate(); + } +} + +/*! + \internal + Copies the list of commands previously queued up. Called by Q3DSRenderer::synchronize(). + */ +void Q3DSStudio3D::getCommands(bool emitInitialize, CommandQueue &renderQueue) +{ + if (emitInitialize) + m_emitRunningChange = true; + + renderQueue.copyCommands(m_pendingCommands); + m_pendingCommands.clear(false); +} + +/*! + \internal + */ +void Q3DSStudio3D::mousePressEvent(QMouseEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreMouseEvents)) + return; + + if (m_pixelRatio != 1.0) { + QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio, + event->button(), event->buttons(), event->modifiers()); + m_presentation->mousePressEvent(&scaledEvent); + } else { + m_presentation->mousePressEvent(event); + } +} + +/*! + \internal + */ +void Q3DSStudio3D::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreMouseEvents)) + return; + + if (m_pixelRatio != 1.0) { + QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio, + event->button(), event->buttons(), event->modifiers()); + m_presentation->mouseReleaseEvent(&scaledEvent); + } else { + m_presentation->mouseReleaseEvent(event); + } +} + +/*! + \internal + */ +void Q3DSStudio3D::mouseMoveEvent(QMouseEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreMouseEvents)) + return; + + if (m_pixelRatio != 1.0) { + QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio, + event->button(), event->buttons(), event->modifiers()); + m_presentation->mouseMoveEvent(&scaledEvent); + } else { + m_presentation->mouseMoveEvent(event); + } +} + +/*! + \internal + */ +void Q3DSStudio3D::wheelEvent(QWheelEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreWheelEvents)) + return; + + m_presentation->wheelEvent(event); +} + +/*! + \internal + */ +void Q3DSStudio3D::keyPressEvent(QKeyEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreKeyboardEvents)) + return; + + m_presentation->keyPressEvent(event); +} + +/*! + \internal + */ +void Q3DSStudio3D::keyReleaseEvent(QKeyEvent *event) +{ + if (m_eventIgnoreFlags.testFlag(IgnoreKeyboardEvents)) + return; + + if (!event->isAutoRepeat()) + m_presentation->keyReleaseEvent(event); +} + +QT_END_NAMESPACE diff --git a/src/api/studio3dqml/q3dsstudio3d_p.h b/src/api/studio3dqml/q3dsstudio3d_p.h new file mode 100644 index 0000000..eed8459 --- /dev/null +++ b/src/api/studio3dqml/q3dsstudio3d_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (c) 2016 NVIDIA CORPORATION. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DS_STUDIO3D_H +#define Q3DS_STUDIO3D_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtStudio3D API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtStudio3D/private/q3dscommandqueue_p.h> +#include <QtGui/qopenglframebufferobject.h> +#include <QtQuick/qquickframebufferobject.h> + +QT_BEGIN_NAMESPACE + +class Q3DSRenderer; +class Q3DSViewerSettings; +class Q3DSPresentationItem; + +class Q3DSStudio3D : public QQuickFramebufferObject +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) + Q_PROPERTY(Q3DSPresentationItem *presentation READ presentation CONSTANT) + Q_PROPERTY(Q3DSViewerSettings *viewerSettings READ viewerSettings CONSTANT) + Q_PROPERTY(QString error READ error NOTIFY errorChanged) + Q_PROPERTY(EventIgnoreFlags ignoredEvents READ ignoredEvents WRITE setIgnoredEvents NOTIFY ignoredEventsChanged) + +public: + enum EventIgnoreFlag { + EnableAllEvents = 0, + IgnoreMouseEvents = 0x01, + IgnoreWheelEvents = 0x02, + IgnoreKeyboardEvents = 0x04, + IgnoreAllInputEvents = IgnoreMouseEvents | IgnoreWheelEvents | IgnoreKeyboardEvents + }; + Q_DECLARE_FLAGS(EventIgnoreFlags, EventIgnoreFlag) + Q_FLAG(EventIgnoreFlags) + + Q3DSStudio3D(); + ~Q3DSStudio3D() override; + + QQuickFramebufferObject::Renderer *createRenderer() const override; + + bool isRunning() const; + Q3DSPresentationItem *presentation() const; + Q3DSViewerSettings *viewerSettings() const; + QString error() const; + void setError(const QString &error); + + void getCommands(bool emitInitialize, CommandQueue &renderQueue); + + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + + EventIgnoreFlags ignoredEvents() const; + void setIgnoredEvents(EventIgnoreFlags flags); + + void componentComplete() override; + +Q_SIGNALS: + void frameUpdate(); + void runningChanged(bool initialized); + void errorChanged(const QString &error); + void ignoredEventsChanged(); + void presentationReady(); + void presentationLoaded(); + +public Q_SLOTS: + void reset(); + +protected Q_SLOTS: + void handleWindowChanged(QQuickWindow *window); + void handleVisibleChanged(); + void tick(); + void requestResponseHandler(const QString &elementPath, CommandType commandType, + void *requestData); +private: + void updateEventMasks(); + +protected: + Q3DSViewerSettings *m_viewerSettings; + Q3DSPresentationItem *m_presentation; + + bool m_emitRunningChange; + bool m_isRunning; + EventIgnoreFlags m_eventIgnoreFlags; + + CommandQueue m_pendingCommands; + qreal m_pixelRatio; + QString m_error; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSStudio3D::EventIgnoreFlags) + +QT_END_NAMESPACE + +#endif // Q3DS_STUDIO3D_H diff --git a/src/api/studio3dqml/qmldir b/src/api/studio3dqml/qmldir new file mode 100644 index 0000000..1eaa8f2 --- /dev/null +++ b/src/api/studio3dqml/qmldir @@ -0,0 +1,3 @@ +module QtStudio3D.OpenGL +plugin declarative_qtstudio3dopengl +classname Q3DSPlugin diff --git a/src/api/studio3dqml/studio3dqml.pro b/src/api/studio3dqml/studio3dqml.pro new file mode 100644 index 0000000..b62a342 --- /dev/null +++ b/src/api/studio3dqml/studio3dqml.pro @@ -0,0 +1,34 @@ +include($$PWD/../../commoninclude.pri) + +QT += qml quick opengl studio3d-private +CONFIG += plugin + +qtHaveModule(multimedia) { +DEFINES += PLATFORM_HAS_QT_MULTIMEDIA_LIB +QT += multimedia +} + +TARGET = qtstudio3dopengl +TARGETPATH = QtStudio3D/OpenGL +IMPORT_VERSION = 2.4 + +SOURCES += \ + q3dsplugin.cpp \ + q3dsstudio3d.cpp \ + q3dsrenderer.cpp \ + q3dspresentationitem.cpp + +HEADERS += \ + q3dsplugin.h \ + q3dsrenderer_p.h \ + q3dsstudio3d_p.h \ + q3dspresentationitem_p.h + +LIBS += \ + -lqt3dsopengl$$qtPlatformTargetSuffix() \ + -lqt3dsqmlstreamer$$qtPlatformTargetSuffix() + +OTHER_FILES += \ + qmldir + +load(qml_plugin) |