diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2019-06-03 14:00:07 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2019-06-03 14:01:18 +0300 |
commit | 01cfc8025119609803b3fc00b1e8ca5bc56e84d0 (patch) | |
tree | c41480bbb06377f3a79d13c465a53435cccb9de4 /src/Runtime/ogl-runtime/src/api/studio3dqml/q3dsrenderer.cpp | |
parent | c36739204786bd90df6e2ef5a8be78c980240bdd (diff) | |
parent | 089d283afb8cde5024e60c29eeeeefc5c3c80543 (diff) |
Merge branch '2.4'
Change-Id: Ib7de8e52a477c06599ced87bb973a5abd25549c4
Diffstat (limited to 'src/Runtime/ogl-runtime/src/api/studio3dqml/q3dsrenderer.cpp')
-rw-r--r-- | src/Runtime/ogl-runtime/src/api/studio3dqml/q3dsrenderer.cpp | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/src/Runtime/ogl-runtime/src/api/studio3dqml/q3dsrenderer.cpp b/src/Runtime/ogl-runtime/src/api/studio3dqml/q3dsrenderer.cpp new file mode 100644 index 00000000..bbfee502 --- /dev/null +++ b/src/Runtime/ogl-runtime/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(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_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); + + 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 |