From 78923902089d0a70c096e5f47f42e24fbcd4fa56 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 1 Jul 2014 19:19:56 +0200 Subject: Expose the scene graph publically through a QSGEngine class This change wraps QSGRenderContext and QSGContext in a new QSGEngine class, and expose a public interface of QSGRenderer through a QSGAbstractRenderer to make it usable on a standalone window or FBO. Change-Id: I2d41187472424f5ea64650a006bcd61f2711f6b9 Reviewed-by: Gunnar Sletta --- examples/quick/scenegraph/scenegraph.pro | 1 + examples/quick/scenegraph/sgengine/face-smile.png | Bin 0 -> 15408 bytes examples/quick/scenegraph/sgengine/main.cpp | 52 +++++ examples/quick/scenegraph/sgengine/sgengine.pro | 11 + examples/quick/scenegraph/sgengine/sgengine.qrc | 5 + examples/quick/scenegraph/sgengine/window.cpp | 241 ++++++++++++++++++++++ examples/quick/scenegraph/sgengine/window.h | 85 ++++++++ 7 files changed, 395 insertions(+) create mode 100644 examples/quick/scenegraph/sgengine/face-smile.png create mode 100644 examples/quick/scenegraph/sgengine/main.cpp create mode 100644 examples/quick/scenegraph/sgengine/sgengine.pro create mode 100644 examples/quick/scenegraph/sgengine/sgengine.qrc create mode 100644 examples/quick/scenegraph/sgengine/window.cpp create mode 100644 examples/quick/scenegraph/sgengine/window.h (limited to 'examples/quick') diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index 4354954445..85ae25907a 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs SUBDIRS += \ customgeometry \ openglunderqml \ + sgengine \ simplematerial \ textureinsgnode \ textureinthread \ diff --git a/examples/quick/scenegraph/sgengine/face-smile.png b/examples/quick/scenegraph/sgengine/face-smile.png new file mode 100644 index 0000000000..3d66d72578 Binary files /dev/null and b/examples/quick/scenegraph/sgengine/face-smile.png differ diff --git a/examples/quick/scenegraph/sgengine/main.cpp b/examples/quick/scenegraph/sgengine/main.cpp new file mode 100644 index 0000000000..9a9450710b --- /dev/null +++ b/examples/quick/scenegraph/sgengine/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/sgengine/sgengine.pro b/examples/quick/scenegraph/sgengine/sgengine.pro new file mode 100644 index 0000000000..c6507a4982 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/sgengine.pro @@ -0,0 +1,11 @@ +QT += quick + +HEADERS += window.h +SOURCES += window.cpp main.cpp +RESOURCES += \ + sgengine.qrc \ + ../../shared/shared.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/sgengine +INSTALLS += target diff --git a/examples/quick/scenegraph/sgengine/sgengine.qrc b/examples/quick/scenegraph/sgengine/sgengine.qrc new file mode 100644 index 0000000000..5d55bcfb6f --- /dev/null +++ b/examples/quick/scenegraph/sgengine/sgengine.qrc @@ -0,0 +1,5 @@ + + + face-smile.png + + diff --git a/examples/quick/scenegraph/sgengine/window.cpp b/examples/quick/scenegraph/sgengine/window.cpp new file mode 100644 index 0000000000..9af4029165 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/window.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" + +#include +#include +#include +#include +#include +#include +#include + +class Item { +public: + Item(QSGNode *parentNode, QSGTexture *texture, const QPointF &fromPos, const QPointF &toPos) { + textureNode = new QSGSimpleTextureNode; + textureNode->setRect(QRect(QPoint(), texture->textureSize())); + textureNode->setTexture(texture); + + transformNode = new QSGTransformNode; + transformNode->setFlag(QSGNode::OwnedByParent, false); + transformNode->appendChildNode(textureNode); + parentNode->appendChildNode(transformNode); + + int duration = qrand() / float(RAND_MAX) * 400 + 800; + rotAnimation.setStartValue(qrand() / float(RAND_MAX) * 720 - 180); + rotAnimation.setEndValue(qrand() / float(RAND_MAX) * 720 - 180); + rotAnimation.setDuration(duration); + rotAnimation.start(); + + posAnimation.setStartValue(fromPos); + posAnimation.setEndValue(toPos); + posAnimation.setDuration(duration); + posAnimation.start(); + } + + ~Item() { + delete transformNode; + } + + bool isDone() const { return posAnimation.state() != QAbstractAnimation::Running; } + + void sync() { + QPointF currentPos = posAnimation.currentValue().toPointF(); + QPointF center = textureNode->rect().center(); + QMatrix4x4 m; + m.translate(currentPos.x(), currentPos.y()); + m.translate(center.x(), center.y()); + m.rotate(rotAnimation.currentValue().toFloat(), 0, 0, 1); + m.translate(-center.x(), -center.y()); + transformNode->setMatrix(m); + } + +private: + QSGTransformNode *transformNode; + QSGSimpleTextureNode *textureNode; + QVariantAnimation posAnimation; + QVariantAnimation rotAnimation; +}; + +Window::Window() + : m_initialized(false) + , m_context(new QOpenGLContext) + , m_sgEngine(new QSGEngine) + , m_sgRootNode(new QSGRootNode) +{ + setSurfaceType(QWindow::OpenGLSurface); + QRect g(0, 0, 640, 480); + g.moveCenter(screen()->geometry().center()); + setGeometry(g); + setTitle(QStringLiteral("Click me!")); + + QSurfaceFormat format; + format.setDepthBufferSize(16); + setFormat(format); + m_context->setFormat(format); + m_context->create(); + + m_animationDriver.install(); + connect(&m_animationDriver, &QAnimationDriver::started, this, &Window::update); +} + +Window::~Window() +{ +} + +void Window::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == m_updateTimer.timerId()) { + m_updateTimer.stop(); + + if (!m_context->makeCurrent(this)) + return; + + if (!m_initialized) + initialize(); + + sync(); + render(); + + if (m_animationDriver.isRunning()) { + m_animationDriver.advance(); + update(); + } + } else + QWindow::timerEvent(e); +} + +void Window::exposeEvent(QExposeEvent *) +{ + if (isExposed()) + update(); + else + invalidate(); +} + +void Window::mousePressEvent(QMouseEvent *) +{ + addItems(); +} + +void Window::keyPressEvent(QKeyEvent *) +{ + addItems(); +} + +void Window::addItems() +{ + if (!m_initialized) + return; + + QSGTexture *textures[] = { m_smileTexture.data(), m_qtTexture.data() }; + for (int i = 0; i < 50; ++i) { + QSGTexture *tex = textures[i%2]; + QPointF fromPos(-tex->textureSize().width(), qrand() / float(RAND_MAX) * (height() - tex->textureSize().height())); + QPointF toPos(width(), qrand() / float(RAND_MAX) * height() * 1.5 - height() * 0.25); + m_items.append(QSharedPointer(new Item(m_sgRootNode.data(), tex, fromPos, toPos))); + } + update(); +} + +void Window::update() +{ + if (!m_updateTimer.isActive()) + m_updateTimer.start(0, this); +} + +void Window::sync() +{ + QList > validItems; + foreach (QSharedPointer item, m_items) { + if (!item->isDone()) { + validItems.append(item); + item->sync(); + } + } + m_items.swap(validItems); +} + +void Window::render() +{ + m_sgRenderer->setDeviceRect(size()); + m_sgRenderer->setViewportRect(size()); + m_sgRenderer->setProjectionMatrixToRect(QRectF(QPointF(), size())); + m_sgRenderer->renderScene(); + + m_context->swapBuffers(this); +} + +void Window::initialize() +{ + m_sgEngine->initialize(m_context.data()); + m_sgRenderer.reset(m_sgEngine->createRenderer()); + m_sgRenderer->setRootNode(m_sgRootNode.data()); + m_sgRenderer->setClearColor(QColor(32, 32, 32)); + + // With QSGEngine::createTextureFromId + GLuint glTexture; + glGenTextures(1, &glTexture); + glBindTexture(GL_TEXTURE_2D, glTexture); + QImage smile = QImage(":/scenegraph/sgengine/face-smile.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); + smile = smile.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(!smile.isNull()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smile.width(), smile.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, smile.constBits()); + m_smileTexture.reset(m_sgEngine->createTextureFromId(glTexture, smile.size(), QFlag(QSGEngine::TextureOwnsGLTexture | QSGEngine::TextureHasAlphaChannel))); + + // With QSGEngine::createTextureFromImage + QImage qtLogo = QImage(":/shared/images/qt-logo.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); + Q_ASSERT(!qtLogo.isNull()); + m_qtTexture.reset(m_sgEngine->createTextureFromImage(qtLogo)); + m_initialized = true; +} + +void Window::invalidate() +{ + m_updateTimer.stop(); + m_items.clear(); + m_smileTexture.reset(); + m_qtTexture.reset(); + m_sgRenderer.reset(); + m_sgEngine->invalidate(); + m_initialized = false; +} diff --git a/examples/quick/scenegraph/sgengine/window.h b/examples/quick/scenegraph/sgengine/window.h new file mode 100644 index 0000000000..2b239ea430 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/window.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +class Item; +QT_FORWARD_DECLARE_CLASS(QSGAbstractRenderer) +QT_FORWARD_DECLARE_CLASS(QSGEngine) +QT_FORWARD_DECLARE_CLASS(QSGRootNode) +QT_FORWARD_DECLARE_CLASS(QSGTexture) + +class Window : public QWindow +{ + Q_OBJECT +public: + Window(); + ~Window(); + + void addItems(); + void update(); + +private: + void timerEvent(QTimerEvent *); + void exposeEvent(QExposeEvent *); + void mousePressEvent(QMouseEvent *); + void keyPressEvent(QKeyEvent *); + + void sync(); + void render(); + void initialize(); + void invalidate(); + + bool m_initialized; + QScopedPointer m_context; + QScopedPointer m_sgEngine; + QScopedPointer m_sgRootNode; + QScopedPointer m_sgRenderer; + QScopedPointer m_smileTexture; + QScopedPointer m_qtTexture; + QList > m_items; + + QBasicTimer m_updateTimer; + QAnimationDriver m_animationDriver; +}; -- cgit v1.2.3