summaryrefslogtreecommitdiffstats
path: root/src/api/studio3dqml/q3dsrenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/studio3dqml/q3dsrenderer.cpp')
-rw-r--r--src/api/studio3dqml/q3dsrenderer.cpp458
1 files changed, 458 insertions, 0 deletions
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