diff options
31 files changed, 1275 insertions, 134 deletions
diff --git a/.qmake.conf b/.qmake.conf index 0dcf6d9b0..138038d54 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.9.1 +MODULE_VERSION = 5.10.0 diff --git a/src/core/qscene.cpp b/src/core/qscene.cpp index 043b3f11b..b5895c7aa 100644 --- a/src/core/qscene.cpp +++ b/src/core/qscene.cpp @@ -222,13 +222,8 @@ bool QScene::hasEntityForComponent(QNodeId componentUuid, QNodeId entityUuid) { Q_D(QScene); QReadLocker lock(&d->m_lock); - auto it = d->m_componentToEntities.find(componentUuid); - while (it != d->m_componentToEntities.end() && it.key() == componentUuid) { - if (it.value() == entityUuid) - return true; - ++it; - } - return false; + const auto range = d->m_componentToEntities.equal_range(componentUuid); + return std::find(range.first, range.second, entityUuid) != range.second; } QScene::NodePropertyTrackData QScene::lookupNodePropertyTrackData(QNodeId id) const diff --git a/src/core/services/qdownloadhelperservice.cpp b/src/core/services/qdownloadhelperservice.cpp new file mode 100644 index 000000000..231e9172b --- /dev/null +++ b/src/core/services/qdownloadhelperservice.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdownloadhelperservice_p.h" +#include "qdownloadnetworkworker_p.h" +#include <Qt3DCore/QAspectEngine> +#include <Qt3DCore/private/qabstractserviceprovider_p.h> +#include <Qt3DCore/private/qaspectengine_p.h> +#include <Qt3DCore/private/qaspectthread_p.h> +#include <Qt3DCore/private/qaspectmanager_p.h> +#include <Qt3DCore/private/qservicelocator_p.h> + +#include <QFile> + +QT_BEGIN_NAMESPACE + +namespace Qt3DCore { + +QDownloadRequest::QDownloadRequest(const QUrl &url) + : m_url(url) + , m_succeeded(false) + , m_cancelled(false) +{ + +} + +QDownloadRequest::~QDownloadRequest() +{ + +} + +void QDownloadRequest::onDownloaded() +{ + // this is called in dl thread. It's an opportunity to do long running tasks + // like loading the data into a QImage +} + + +class Q_AUTOTEST_EXPORT QDownloadHelperServicePrivate : public QAbstractServiceProviderPrivate +{ +public: + explicit QDownloadHelperServicePrivate(const QString &description); + ~QDownloadHelperServicePrivate(); + + void init(); + void shutdown(); + void _q_onRequestCompleted(const QDownloadRequestPtr &request); + + Q_DECLARE_PUBLIC(QDownloadHelperService) + + QThread *m_downloadThread; + QDownloadNetworkWorker *m_downloadWorker; +}; + + +QDownloadHelperServicePrivate::QDownloadHelperServicePrivate(const QString &description) + : QAbstractServiceProviderPrivate(QServiceLocator::DownloadHelperService, description) + , m_downloadThread(nullptr) + , m_downloadWorker(nullptr) +{ +} + +QDownloadHelperServicePrivate::~QDownloadHelperServicePrivate() +{ +} + +void QDownloadHelperServicePrivate::init() +{ + Q_Q(QDownloadHelperService); + m_downloadThread = new QThread(q); + m_downloadWorker = new QDownloadNetworkWorker; + m_downloadWorker->moveToThread(m_downloadThread); + QObject::connect(m_downloadWorker, SIGNAL(requestDownloaded(const Qt3DCore::QDownloadRequestPtr &)), + q, SLOT(_q_onRequestCompleted(const Qt3DCore::QDownloadRequestPtr &))); + m_downloadThread->start(); +} + +void QDownloadHelperServicePrivate::shutdown() +{ + m_downloadWorker->cancelAllRequests(); + m_downloadThread->exit(); + m_downloadThread->wait(); + m_downloadWorker->deleteLater(); +} + +void QDownloadHelperServicePrivate::_q_onRequestCompleted(const Qt3DCore::QDownloadRequestPtr &request) +{ + request->onCompleted(); +} + + +QDownloadHelperService::QDownloadHelperService(const QString &description) + : QAbstractServiceProvider(*new QDownloadHelperServicePrivate(description)) +{ + Q_D(QDownloadHelperService); + d->init(); + qRegisterMetaType<Qt3DCore::QDownloadRequestPtr>(); +} + +QDownloadHelperService::~QDownloadHelperService() +{ + Q_D(QDownloadHelperService); + d->shutdown(); +} + +void QDownloadHelperService::submitRequest(const Qt3DCore::QDownloadRequestPtr &request) +{ + Q_D(QDownloadHelperService); + + if (isLocal(request->url())) { + QFile file(urlToLocalFileOrQrc(request->url())); + if (file.open(QIODevice::ReadOnly)) { + request->m_data = file.readAll(); + file.close(); + request->m_succeeded = true; + } else { + request->m_succeeded = false; + } + request->onCompleted(); + } else { + emit d->m_downloadWorker->submitRequest(request); + } +} + +void QDownloadHelperService::cancelRequest(const Qt3DCore::QDownloadRequestPtr &request) +{ + Q_D(QDownloadHelperService); + request->m_cancelled = true; + emit d->m_downloadWorker->cancelRequest(request); +} + +void QDownloadHelperService::cancelAllRequests() +{ + Q_D(QDownloadHelperService); + emit d->m_downloadWorker->cancelAllRequests(); +} + +QString QDownloadHelperService::urlToLocalFileOrQrc(const QUrl &url) +{ + const QString scheme(url.scheme().toLower()); + if (scheme == QLatin1String("qrc")) { + if (url.authority().isEmpty()) + return QLatin1Char(':') + url.path(); + return QString(); + } + +#if defined(Q_OS_ANDROID) + if (scheme == QLatin1String("assets")) { + if (url.authority().isEmpty()) + return url.toString(); + return QString(); + } +#endif + + return url.toLocalFile(); +} + +QDownloadHelperService *QDownloadHelperService::getService(QAspectEngine *engine) +{ + auto enginePrivate = Qt3DCore::QAspectEnginePrivate::get(engine); + return enginePrivate->m_aspectThread->aspectManager()->serviceLocator()->downloadHelperService(); +} + +bool QDownloadHelperService::isLocal(const QUrl &url) +{ + const QString scheme(url.scheme().toLower()); + if (scheme == QLatin1String("file") || scheme == QLatin1String("qrc")) + return true; +#if defined(Q_OS_ANDROID) + if (scheme == QLatin1String("assets")) + return true; +#endif + return false; +} + +} // namespace Qt3DCore + +QT_END_NAMESPACE + +#include "moc_qdownloadhelperservice_p.cpp" diff --git a/src/core/services/qdownloadhelperservice_p.h b/src/core/services/qdownloadhelperservice_p.h new file mode 100644 index 000000000..780ed4806 --- /dev/null +++ b/src/core/services/qdownloadhelperservice_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DCORE_QDOWNLOADHELPERSERVICE_P_H +#define QT3DCORE_QDOWNLOADHELPERSERVICE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QSharedPointer> +#include <QObject> +#include <QUrl> + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DCore/qt3dcore_global.h> +#include <Qt3DCore/private/qservicelocator_p.h> + +QT_BEGIN_NAMESPACE + +class QThread; +class QNetworkAccessManager; +class QNetworkReply; + +namespace Qt3DCore { + +class QAspectEngine; +class QDownloadNetworkWorker; +class QDownloadHelperService; +class QDownloadHelperServicePrivate; + +class QT3DCORESHARED_EXPORT QDownloadRequest +{ +public: + QDownloadRequest(const QUrl &url); + virtual ~QDownloadRequest(); + + QUrl url() const { return m_url; } + bool succeeded() const { return m_succeeded; } + bool cancelled() const { return m_cancelled; } + + virtual void onDownloaded(); // this is called in dl thread + virtual void onCompleted() = 0; // this is called in job thread + +protected: + QUrl m_url; + QByteArray m_data; + +private: + friend class QDownloadNetworkWorker; + friend class QDownloadHelperService; + bool m_succeeded; + bool m_cancelled; +}; + +typedef QSharedPointer<QDownloadRequest> QDownloadRequestPtr; + + +class QT3DCORESHARED_EXPORT QDownloadHelperService : public QAbstractServiceProvider +{ + Q_OBJECT +public: + explicit QDownloadHelperService(const QString &description = QString()); + ~QDownloadHelperService(); + + void submitRequest(const QDownloadRequestPtr &request); + void cancelRequest(const QDownloadRequestPtr &request); + void cancelAllRequests(); + + static QString urlToLocalFileOrQrc(const QUrl &url); + static bool isLocal(const QUrl &url); + static QDownloadHelperService *getService(QAspectEngine *engine); + +private: + Q_DECLARE_PRIVATE(QDownloadHelperService) + Q_PRIVATE_SLOT(d_func(), void _q_onRequestCompleted(const Qt3DCore::QDownloadRequestPtr &)) +}; + +typedef QSharedPointer<QDownloadHelperService> QDownloadHelperServicePtr; + +} // namespace Qt3DCore + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Qt3DCore::QDownloadRequestPtr) // LCOV_EXCL_LINE + +#endif // QT3DCORE_QDOWNLOADHELPERSERVICE_P_H diff --git a/src/core/services/qdownloadhelperservice_p_p.h b/src/core/services/qdownloadhelperservice_p_p.h new file mode 100644 index 000000000..202ae5236 --- /dev/null +++ b/src/core/services/qdownloadhelperservice_p_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DCORE_QDOWNLOADHELPERSERVICE_P_P_H +#define QT3DCORE_QDOWNLOADHELPERSERVICE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QSharedPointer> +#include <QObject> +#include <QUrl> + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DCore/private/qservicelocator_p.h> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> +#include <Qt3DCore/private/qabstractserviceprovider_p.h> + +QT_BEGIN_NAMESPACE + +class QThread; +class QNetworkAccessManager; +class QNetworkReply; + +namespace Qt3DCore { + + +} // namespace Qt3DCore + +QT_END_NAMESPACE + +#endif // QT3DCORE_QDOWNLOADHELPERSERVICE_P_P_H diff --git a/src/core/services/qdownloadnetworkworker.cpp b/src/core/services/qdownloadnetworkworker.cpp new file mode 100644 index 000000000..c728a1779 --- /dev/null +++ b/src/core/services/qdownloadnetworkworker.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdownloadhelperservice_p.h" +#include "qdownloadnetworkworker_p.h" + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QDataStream> + +QT_BEGIN_NAMESPACE + +namespace Qt3DCore { + +QDownloadNetworkWorker::QDownloadNetworkWorker(QObject *parent) + : QObject(parent) + , m_networkManager(nullptr) +{ + connect(this, &QDownloadNetworkWorker::submitRequest, + this, &QDownloadNetworkWorker::onRequestSubmited); + connect(this, &QDownloadNetworkWorker::cancelRequest, + this, &QDownloadNetworkWorker::onRequestCancelled); + connect(this, &QDownloadNetworkWorker::cancelAllRequests, + this, &QDownloadNetworkWorker::onAllRequestsCancelled); +} + +void QDownloadNetworkWorker::onRequestSubmited(const QDownloadRequestPtr &request) +{ + QMutexLocker l(&m_mutex); + if (!m_networkManager) { + m_networkManager = new QNetworkAccessManager(this); + connect(m_networkManager, &QNetworkAccessManager::finished, + this, &QDownloadNetworkWorker::onRequestFinished); + } + auto reply = m_networkManager->get(QNetworkRequest(request->url())); + m_requests << QPair<QDownloadRequestPtr, QNetworkReply *>(request, reply); + connect(reply, &QNetworkReply::downloadProgress, this, &QDownloadNetworkWorker::onDownloadProgressed); +} + +void QDownloadNetworkWorker::onRequestCancelled(const QDownloadRequestPtr &request) +{ + QMutexLocker l(&m_mutex); + auto it = std::find_if(m_requests.begin(), m_requests.end(), + [request](QPair<QDownloadRequestPtr, QNetworkReply *> e) { + return e.first == request; + }); + if (it == m_requests.end()) + return; + + (*it).first->m_cancelled = true; + (*it).second->abort(); +} + +void QDownloadNetworkWorker::onAllRequestsCancelled() +{ + QMutexLocker l(&m_mutex); + for (auto e: qAsConst(m_requests)) { + e.first->m_cancelled = true; + e.second->abort(); + } + m_requests.clear(); +} + +void QDownloadNetworkWorker::onRequestFinished(QNetworkReply *reply) +{ + QMutexLocker l(&m_mutex); + auto it = std::find_if(m_requests.begin(), m_requests.end(), + [reply](QPair<QDownloadRequestPtr, QNetworkReply *> e) { + return e.second == reply; + }); + if (it == m_requests.end()) + return; + + auto request = (*it).first; + if (reply->error() == QNetworkReply::NoError) { + request->m_succeeded = true; + } + request->onDownloaded(); + emit requestDownloaded(request); + + m_requests.erase(it); +} + +void QDownloadNetworkWorker::onDownloadProgressed(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_UNUSED(bytesReceived); + Q_UNUSED(bytesTotal); + // TODO forward progress details somewhere + + QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); + if (!reply) + return; + + QMutexLocker l(&m_mutex); + auto it = std::find_if(m_requests.begin(), m_requests.end(), + [reply](QPair<QDownloadRequestPtr, QNetworkReply *> e) { + return e.second == reply; + }); + if (it == m_requests.end()) + return; + + auto request = (*it).first; + QDataStream stream(&request->m_data, QIODevice::Append); + QByteArray data = reply->readAll(); + stream.writeRawData(data.data(), data.size()); +} + +} // namespace Qt3DCore + +QT_END_NAMESPACE + diff --git a/src/core/services/qdownloadnetworkworker_p.h b/src/core/services/qdownloadnetworkworker_p.h new file mode 100644 index 000000000..faecbca2f --- /dev/null +++ b/src/core/services/qdownloadnetworkworker_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DCORE_QDOWNLOADNETWORKWORKER_P_H +#define QT3DCORE_QDOWNLOADNETWORKWORKER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QSharedPointer> +#include <QObject> +#include <QUrl> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +class QThread; +class QNetworkAccessManager; +class QNetworkReply; + +namespace Qt3DCore { + +class QDownloadRequest; +typedef QSharedPointer<QDownloadRequest> QDownloadRequestPtr; + +class QDownloadNetworkWorker : public QObject +{ + Q_OBJECT +public: + QDownloadNetworkWorker(QObject *parent = nullptr); + +signals: + void submitRequest(const Qt3DCore::QDownloadRequestPtr &request); + void cancelRequest(const Qt3DCore::QDownloadRequestPtr &request); + void cancelAllRequests(); + + void requestDownloaded(const Qt3DCore::QDownloadRequestPtr &request); + +private Q_SLOTS: + void onRequestSubmited(const Qt3DCore::QDownloadRequestPtr &request); + void onRequestCancelled(const Qt3DCore::QDownloadRequestPtr &request); + void onAllRequestsCancelled(); + void onRequestFinished(QNetworkReply *reply); + void onDownloadProgressed(qint64 bytesReceived, qint64 bytesTotal); + +private: + QNetworkAccessManager *m_networkManager; + QVector< QPair<QDownloadRequestPtr, QNetworkReply *> > m_requests; + QMutex m_mutex; +}; + +} // namespace Qt3DCore + +QT_END_NAMESPACE + +#endif // QT3DCORE_QDOWNLOADNETWORKWORKER_P_H diff --git a/src/core/services/qservicelocator.cpp b/src/core/services/qservicelocator.cpp index 9d12cfe2c..bdcb4a521 100644 --- a/src/core/services/qservicelocator.cpp +++ b/src/core/services/qservicelocator.cpp @@ -43,9 +43,11 @@ #include <Qt3DCore/private/nullservices_p.h> #include <Qt3DCore/private/qabstractserviceprovider_p.h> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> #include <Qt3DCore/private/qeventfilterservice_p.h> #include <Qt3DCore/private/qtickclockservice_p.h> + QT_BEGIN_NAMESPACE namespace Qt3DCore { @@ -96,6 +98,7 @@ public: NullOpenGLInformationService m_nullOpenGLInfo; QTickClockService m_defaultFrameAdvanceService; QEventFilterService m_eventFilterService; + QDownloadHelperService m_downloadHelperService; int m_nonNullDefaultServices; }; @@ -229,6 +232,12 @@ QEventFilterService *QServiceLocator::eventFilterService() return static_cast<QEventFilterService *>(d->m_services.value(EventFilterService, &d->m_eventFilterService)); } +QDownloadHelperService *QServiceLocator::downloadHelperService() +{ + Q_D(QServiceLocator); + return static_cast<QDownloadHelperService *>(d->m_services.value(DownloadHelperService, &d->m_downloadHelperService)); +} + /* \internal */ @@ -244,6 +253,8 @@ QAbstractServiceProvider *QServiceLocator::_q_getServiceHelper(int type) return frameAdvanceService(); case EventFilterService: return eventFilterService(); + case DownloadHelperService: + return downloadHelperService(); default: return d->m_services.value(type, nullptr); } diff --git a/src/core/services/qservicelocator_p.h b/src/core/services/qservicelocator_p.h index 1c5db4a5b..9534b33ce 100644 --- a/src/core/services/qservicelocator_p.h +++ b/src/core/services/qservicelocator_p.h @@ -84,6 +84,7 @@ class QOpenGLInformationService; class QSystemInformationService; class QServiceLocatorPrivate; class QEventFilterService; +class QDownloadHelperService; class QT3DCORESHARED_EXPORT QServiceLocator { @@ -97,6 +98,7 @@ public: CollisionService, FrameAdvanceService, EventFilterService, + DownloadHelperService, #if !defined(Q_QDOC) DefaultServiceCount, // Add additional default services before here #endif @@ -120,6 +122,7 @@ public: QOpenGLInformationService *openGLInformation(); QAbstractFrameAdvanceService *frameAdvanceService(); QEventFilterService *eventFilterService(); + QDownloadHelperService *downloadHelperService(); private: Q_DISABLE_COPY(QServiceLocator) diff --git a/src/core/services/services.pri b/src/core/services/services.pri index ae0cfd9f8..f311b8329 100644 --- a/src/core/services/services.pri +++ b/src/core/services/services.pri @@ -5,7 +5,9 @@ SOURCES += \ $$PWD/qopenglinformationservice.cpp \ $$PWD/qtickclockservice.cpp \ $$PWD/qabstractframeadvanceservice.cpp \ - $$PWD/qeventfilterservice.cpp + $$PWD/qeventfilterservice.cpp \ + $$PWD/qdownloadhelperservice.cpp \ + $$PWD/qdownloadnetworkworker.cpp HEADERS += \ $$PWD/qservicelocator_p.h \ @@ -18,6 +20,8 @@ HEADERS += \ $$PWD/qtickclockservice_p.h \ $$PWD/qabstractframeadvanceservice_p.h \ $$PWD/qabstractframeadvanceservice_p_p.h \ - $$PWD/qeventfilterservice_p.h + $$PWD/qeventfilterservice_p.h \ + $$PWD/qdownloadhelperservice_p.h \ + $$PWD/qdownloadnetworkworker_p.h INCLUDEPATH += $$PWD diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp index 6f61d2ed9..78e230969 100644 --- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp +++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp @@ -1072,8 +1072,8 @@ void GLTFImporter::cleanup() m_shaderPaths.clear(); delete_if_without_parent(m_programs); m_programs.clear(); - for (auto it = m_techniqueParameters.begin(); it != m_techniqueParameters.end(); ++it) - delete_if_without_parent(it.value()); + for (auto params : qAsConst(m_techniqueParameters)) + delete_if_without_parent(params); m_techniqueParameters.clear(); delete_if_without_parent(m_techniques); m_techniques.clear(); diff --git a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp index 4b1d0fb40..7921fce64 100644 --- a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp +++ b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp @@ -1253,8 +1253,7 @@ bool GLTFExporter::saveScene() m_obj["meshes"] = meshes; QJsonObject cameras; - for (auto it = m_cameraInfo.begin(); it != m_cameraInfo.end(); ++it) { - const auto &camInfo = it.value(); + for (auto camInfo : qAsConst(m_cameraInfo)) { QJsonObject camera; QJsonObject proj; proj["znear"] = camInfo.znear; diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp index 1f88cb006..11c03e7db 100644 --- a/src/render/frontend/qrenderaspect.cpp +++ b/src/render/frontend/qrenderaspect.cpp @@ -140,6 +140,7 @@ #include <Qt3DCore/qtransform.h> #include <Qt3DCore/qnode.h> +#include <Qt3DCore/QAspectEngine> #include <Qt3DCore/private/qservicelocator_p.h> #include <QDebug> @@ -186,6 +187,17 @@ QRenderAspectPrivate::~QRenderAspectPrivate() m_instances.removeAll(this); } +QRenderAspectPrivate *QRenderAspectPrivate::findPrivate(Qt3DCore::QAspectEngine *engine) +{ + const QVector<QAbstractAspect*> aspects = engine->aspects(); + for (QAbstractAspect* aspect : aspects) { + QRenderAspect *renderAspect = qobject_cast<QRenderAspect *>(aspect); + if (renderAspect) + return static_cast<QRenderAspectPrivate *>(renderAspect->d_ptr.data()); + } + return nullptr; +} + /*! \internal */ void QRenderAspectPrivate::registerBackendTypes() { @@ -505,7 +517,8 @@ void QRenderAspect::onRegistered() advanceService); } - d->m_renderer->setServices(d->services()); + if (d->services()) + d->m_renderer->setServices(d->services()); d->m_initialized = true; } diff --git a/src/render/frontend/qrenderaspect_p.h b/src/render/frontend/qrenderaspect_p.h index 4f9983d32..b8c8538ee 100644 --- a/src/render/frontend/qrenderaspect_p.h +++ b/src/render/frontend/qrenderaspect_p.h @@ -83,6 +83,8 @@ public: Q_DECLARE_PUBLIC(QRenderAspect) + static QRenderAspectPrivate* findPrivate(Qt3DCore::QAspectEngine *engine); + void registerBackendTypes(); void unregisterBackendTypes(); void loadSceneParsers(); diff --git a/src/render/geometry/qmesh.cpp b/src/render/geometry/qmesh.cpp index 1b5d02723..40a4c2f52 100644 --- a/src/render/geometry/qmesh.cpp +++ b/src/render/geometry/qmesh.cpp @@ -45,11 +45,23 @@ #include <QFile> #include <QFileInfo> #include <QScopedPointer> -#include <Qt3DRender/private/qgeometryloaderinterface_p.h> +#include <QMimeDatabase> +#include <QMimeType> +#include <QtCore/QBuffer> +#include <Qt3DRender/QRenderAspect> +#include <Qt3DCore/QAspectEngine> #include <Qt3DCore/qpropertyupdatedchange.h> +#include <Qt3DCore/private/qscene_p.h> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> +#include <Qt3DRender/private/qrenderaspect_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/qgeometryloaderinterface_p.h> #include <Qt3DRender/private/renderlogging_p.h> #include <Qt3DRender/private/qurlhelper_p.h> #include <Qt3DRender/private/qgeometryloaderfactory_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> + +#include <algorithm> QT_BEGIN_NAMESPACE @@ -62,6 +74,24 @@ QMeshPrivate::QMeshPrivate() { } +QMeshPrivate *QMeshPrivate::get(QMesh *q) +{ + return q->d_func(); +} + +void QMeshPrivate::setScene(Qt3DCore::QScene *scene) +{ + QGeometryRendererPrivate::setScene(scene); + updateFunctor(); +} + +void QMeshPrivate::updateFunctor() +{ + Q_Q(QMesh); + Qt3DCore::QAspectEngine *engine = m_scene ? m_scene->engine() : nullptr; + q->setGeometryFactory(QGeometryFactoryPtr(new MeshLoaderFunctor(q, engine))); +} + /*! * \qmltype Mesh * \instantiates Qt3DRender::QMesh @@ -141,8 +171,7 @@ void QMesh::setSource(const QUrl& source) if (d->m_source == source) return; d->m_source = source; - // update the functor - QGeometryRenderer::setGeometryFactory(QGeometryFactoryPtr(new MeshFunctor(d->m_source, d->m_meshName))); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit sourceChanged(source); blockNotifications(blocked); @@ -165,8 +194,7 @@ void QMesh::setMeshName(const QString &meshName) if (d->m_meshName == meshName) return; d->m_meshName = meshName; - // update the functor - QGeometryRenderer::setGeometryFactory(QGeometryFactoryPtr(new MeshFunctor(d->m_source, d->m_meshName))); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit meshNameChanged(meshName); blockNotifications(blocked); @@ -186,51 +214,89 @@ QString QMesh::meshName() const /*! * \internal */ -MeshFunctor::MeshFunctor(const QUrl &sourcePath, const QString& meshName) +MeshLoaderFunctor::MeshLoaderFunctor(QMesh *mesh, Qt3DCore::QAspectEngine *engine, const QByteArray &sourceData) : QGeometryFactory() - , m_sourcePath(sourcePath) - , m_meshName(meshName) + , m_mesh(mesh->id()) + , m_sourcePath(mesh->source()) + , m_meshName(mesh->meshName()) + , m_engine(engine) + , m_sourceData(sourceData) { } /*! * \internal */ -QGeometry *MeshFunctor::operator()() +QGeometry *MeshLoaderFunctor::operator()() { if (m_sourcePath.isEmpty()) { qCWarning(Render::Jobs) << Q_FUNC_INFO << "Mesh is empty, nothing to load"; return nullptr; } - // TO DO: Handle file download if remote url - QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_sourcePath); + QStringList ext; + if (!Qt3DCore::QDownloadHelperService::isLocal(m_sourcePath)) { + if (m_sourceData.isEmpty()) { + if (m_mesh) { + auto downloadService = Qt3DCore::QDownloadHelperService::getService(m_engine); + Qt3DCore::QDownloadRequestPtr request(new MeshDownloadRequest(m_mesh, m_sourcePath, m_engine)); + downloadService->submitRequest(request); + } + return nullptr; + } - QFileInfo finfo(filePath); - auto ext = finfo.suffix(); - if (ext.isEmpty()) - ext = QLatin1String("obj"); + QMimeDatabase db; + QMimeType mtype = db.mimeTypeForData(m_sourceData); + if (mtype.isValid()) { + ext = mtype.suffixes(); + } + QFileInfo finfo(m_sourcePath.path()); + ext << finfo.suffix(); + ext.removeAll(QLatin1String("")); + if (!ext.contains(QLatin1String("obj"))) + ext << QLatin1String("obj"); + } else { + QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_sourcePath); + QFileInfo finfo(filePath); + if (finfo.suffix().isEmpty()) + ext << QLatin1String("obj"); + else + ext << finfo.suffix(); + } QScopedPointer<QGeometryLoaderInterface> loader; - - loader.reset(qLoadPlugin<QGeometryLoaderInterface, QGeometryLoaderFactory>(geometryLoader(), ext)); + for (QString e: qAsConst(ext)) { + loader.reset(qLoadPlugin<QGeometryLoaderInterface, QGeometryLoaderFactory>(geometryLoader(), e)); + if (loader) + break; + } if (!loader) { - qCWarning(Render::Jobs, "unsupported format encountered (%s)", qPrintable(ext)); + qCWarning(Render::Jobs, "unsupported format encountered (%s)", qPrintable(ext.join(QLatin1String(", ")))); return nullptr; } - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) { - qCDebug(Render::Jobs) << "Could not open file" << filePath << "for reading"; - return nullptr; - } + if (m_sourceData.isEmpty()) { + QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_sourcePath); + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + qCDebug(Render::Jobs) << "Could not open file" << filePath << "for reading"; + return nullptr; + } - qCDebug(Render::Jobs) << Q_FUNC_INFO << "Loading mesh from" << m_sourcePath << " part:" << m_meshName; + if (loader->load(&file, m_meshName)) + return loader->geometry(); + qCWarning(Render::Jobs) << Q_FUNC_INFO << "Mesh loading failure for:" << filePath; + } else { + QT_PREPEND_NAMESPACE(QBuffer) buffer(&m_sourceData); + if (!buffer.open(QIODevice::ReadOnly)) { + return nullptr; + } - if (loader->load(&file, m_meshName)) - return loader->geometry(); + if (loader->load(&buffer, m_meshName)) + return loader->geometry(); - qCWarning(Render::Jobs) << Q_FUNC_INFO << "Mesh loading failure for:" << filePath; + qCWarning(Render::Jobs) << Q_FUNC_INFO << "Mesh loading failure for:" << m_sourcePath; + } return nullptr; } @@ -238,14 +304,47 @@ QGeometry *MeshFunctor::operator()() /*! * \internal */ -bool MeshFunctor::operator ==(const QGeometryFactory &other) const +bool MeshLoaderFunctor::operator ==(const QGeometryFactory &other) const { - const MeshFunctor *otherFunctor = functor_cast<MeshFunctor>(&other); + const MeshLoaderFunctor *otherFunctor = functor_cast<MeshLoaderFunctor>(&other); if (otherFunctor != nullptr) - return (otherFunctor->m_sourcePath == m_sourcePath); + return (otherFunctor->m_sourcePath == m_sourcePath && + otherFunctor->m_sourceData.isEmpty() == m_sourceData.isEmpty() && + otherFunctor->m_engine == m_engine); return false; } +/*! + * \internal + */ +MeshDownloadRequest::MeshDownloadRequest(Qt3DCore::QNodeId mesh, QUrl source, Qt3DCore::QAspectEngine *engine) + : Qt3DCore::QDownloadRequest(source) + , m_mesh(mesh) + , m_engine(engine) +{ + +} + +void MeshDownloadRequest::onCompleted() +{ + if (cancelled() || !succeeded()) + return; + + QRenderAspectPrivate* d_aspect = QRenderAspectPrivate::findPrivate(m_engine); + if (!d_aspect) + return; + + Render::GeometryRenderer *renderer = d_aspect->m_nodeManagers->geometryRendererManager()->lookupResource(m_mesh); + if (!renderer) + return; + + QSharedPointer<MeshLoaderFunctor> functor = qSharedPointerCast<MeshLoaderFunctor>(renderer->geometryFactory()); + functor->m_sourceData = m_data; + + // mark the component as dirty so that the functor runs again in the correct job + d_aspect->m_nodeManagers->geometryRendererManager()->addDirtyGeometryRenderer(m_mesh); +} + } // namespace Qt3DRender QT_END_NAMESPACE diff --git a/src/render/geometry/qmesh_p.h b/src/render/geometry/qmesh_p.h index a621525cc..f7f8079eb 100644 --- a/src/render/geometry/qmesh_p.h +++ b/src/render/geometry/qmesh_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include <Qt3DCore/private/qdownloadhelperservice_p.h> #include <Qt3DRender/private/qgeometryrenderer_p.h> #include <QUrl> @@ -66,23 +67,40 @@ public: QMeshPrivate(); Q_DECLARE_PUBLIC(QMesh) + static QMeshPrivate *get(QMesh *q); + + void setScene(Qt3DCore::QScene *scene) override; + void updateFunctor(); QUrl m_source; QString m_meshName; }; +class Q_AUTOTEST_EXPORT MeshDownloadRequest : public Qt3DCore::QDownloadRequest +{ +public: + MeshDownloadRequest(Qt3DCore::QNodeId mesh, QUrl source, Qt3DCore::QAspectEngine *engine); + + void onCompleted() Q_DECL_OVERRIDE; -class Q_AUTOTEST_EXPORT MeshFunctor : public QGeometryFactory +private: + Qt3DCore::QNodeId m_mesh; + Qt3DCore::QAspectEngine *m_engine; +}; + +class Q_AUTOTEST_EXPORT MeshLoaderFunctor : public QGeometryFactory { public : - MeshFunctor(const QUrl &sourcePath, const QString &meshName = QString()); + MeshLoaderFunctor(QMesh *mesh, Qt3DCore::QAspectEngine *engine, const QByteArray &sourceData = QByteArray()); QGeometry *operator()() Q_DECL_OVERRIDE; bool operator ==(const QGeometryFactory &other) const Q_DECL_OVERRIDE; - QT3D_FUNCTOR(MeshFunctor) + QT3D_FUNCTOR(MeshLoaderFunctor) -private: + Qt3DCore::QNodeId m_mesh; QUrl m_sourcePath; QString m_meshName; + Qt3DCore::QAspectEngine *m_engine; + QByteArray m_sourceData; }; diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp index 7bbab307c..e81836502 100644 --- a/src/render/jobs/calcboundingvolumejob.cpp +++ b/src/render/jobs/calcboundingvolumejob.cpp @@ -216,30 +216,28 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node) return; } - if (positionAttribute) { - Buffer *buf = manager->lookupResource<Buffer, BufferManager>(positionAttribute->bufferId()); - // No point in continuing if the positionAttribute doesn't have a suitable buffer - if (!buf) { - qWarning() << "ObjectPicker position Attribute not referencing a valid buffer"; - return; - } + Buffer *buf = manager->lookupResource<Buffer, BufferManager>(positionAttribute->bufferId()); + // No point in continuing if the positionAttribute doesn't have a suitable buffer + if (!buf) { + qWarning() << "ObjectPicker position Attribute not referencing a valid buffer"; + return; + } - // Buf will be set to not dirty once it's loaded - // in a job executed after this one - // We need to recompute the bounding volume - // If anything in the GeometryRenderer has changed - if (buf->isDirty() || - node->isBoundingVolumeDirty() || - positionAttribute->isDirty() || - geom->isDirty() || - gRenderer->isDirty()) { - - BoundingVolumeCalculator reader(manager); - if (reader.apply(positionAttribute)) { - node->localBoundingVolume()->setCenter(reader.result().center()); - node->localBoundingVolume()->setRadius(reader.result().radius()); - node->unsetBoundingVolumeDirty(); - } + // Buf will be set to not dirty once it's loaded + // in a job executed after this one + // We need to recompute the bounding volume + // If anything in the GeometryRenderer has changed + if (buf->isDirty() || + node->isBoundingVolumeDirty() || + positionAttribute->isDirty() || + geom->isDirty() || + gRenderer->isDirty()) { + + BoundingVolumeCalculator reader(manager); + if (reader.apply(positionAttribute)) { + node->localBoundingVolume()->setCenter(reader.result().center()); + node->localBoundingVolume()->setRadius(reader.result().radius()); + node->unsetBoundingVolumeDirty(); } } } diff --git a/src/render/texture/gltexture.cpp b/src/render/texture/gltexture.cpp index 606681bd5..854789e94 100644 --- a/src/render/texture/gltexture.cpp +++ b/src/render/texture/gltexture.cpp @@ -283,20 +283,24 @@ void GLTexture::setImages(const QVector<Image> &images) void GLTexture::setGenerator(const QTextureGeneratorPtr &generator) { - if (m_dataFunctor != generator) { - if (m_dataFunctor) - m_textureDataManager->releaseData(m_dataFunctor, this); + // Note: we do not compare if the generator is different + // as in some cases we may want to reset the same generator to force a reload + // e.g when using remote urls for textures + if (m_dataFunctor) + m_textureDataManager->releaseData(m_dataFunctor, this); - m_textureData.reset(); - m_dataFunctor = generator; + m_textureData.reset(); + m_dataFunctor = generator; - if (m_dataFunctor) { - m_textureDataManager->requestData(m_dataFunctor, this); - requestUpload(); - } + if (m_dataFunctor) { + m_textureDataManager->requestData(m_dataFunctor, this); + requestUpload(); } } +// Return nullptr if +// - context cannot be obtained +// - texture hasn't yet been loaded QOpenGLTexture *GLTexture::buildGLTexture() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -306,7 +310,9 @@ QOpenGLTexture *GLTexture::buildGLTexture() } if (m_actualTarget == QAbstractTexture::TargetAutomatic) { - qWarning() << Q_FUNC_INFO << "something went wrong, target shouldn't be automatic at this point"; + // If the target is automatic at this point, it means that the texture + // hasn't been loaded yet (case of remote urls) and that loading failed + // and that target format couldn't be deduced return nullptr; } diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp index 7703933a4..1bd002104 100644 --- a/src/render/texture/qabstracttexture.cpp +++ b/src/render/texture/qabstracttexture.cpp @@ -69,6 +69,11 @@ QAbstractTexturePrivate::QAbstractTexturePrivate() { } +QTextureGeneratorPtr QAbstractTexturePrivate::dataFunctor() const +{ + return m_dataFunctor; +} + void QAbstractTexturePrivate::setDataFunctor(const QTextureGeneratorPtr &generator) { if (generator != m_dataFunctor) { diff --git a/src/render/texture/qabstracttexture_p.h b/src/render/texture/qabstracttexture_p.h index c245a78af..a27ae3729 100644 --- a/src/render/texture/qabstracttexture_p.h +++ b/src/render/texture/qabstracttexture_p.h @@ -87,6 +87,7 @@ public : int m_layers; int m_samples; + QTextureGeneratorPtr dataFunctor() const; void setDataFunctor(const QTextureGeneratorPtr &generator); private: diff --git a/src/render/texture/qtexture.cpp b/src/render/texture/qtexture.cpp index f75a47f95..931d66c64 100644 --- a/src/render/texture/qtexture.cpp +++ b/src/render/texture/qtexture.cpp @@ -44,7 +44,17 @@ #include "qtexture.h" #include "qtexture_p.h" #include <QFileInfo> +#include <QMimeDatabase> +#include <QMimeType> #include <qendian.h> +#include <Qt3DCore/private/qscene_p.h> +#include <Qt3DCore/qaspectengine.h> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> +#include <Qt3DRender/private/qrenderaspect_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/texture_p.h> +#include <Qt3DRender/private/qurlhelper_p.h> QT_BEGIN_NAMESPACE @@ -424,9 +434,8 @@ enum CompressedFormatExtension { PKM }; -CompressedFormatExtension texturedCompressedFormat(const QString &source) +CompressedFormatExtension texturedCompressedFormat(const QString &suffix) { - const QString suffix = QFileInfo(source).suffix(); if (suffix == QStringLiteral("pkm")) return PKM; if (suffix == QStringLiteral("dds")) @@ -434,19 +443,14 @@ CompressedFormatExtension texturedCompressedFormat(const QString &source) return None; } -QTextureImageDataPtr setPkmFile(const QString &source) +QTextureImageDataPtr setPkmFile(QIODevice *source) { QTextureImageDataPtr imageData; - QFile f(source); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << source; - return imageData; - } - // ETC1 in PKM, as generated by f.ex. Android's etc1tool + // ETC1 in PKM, as generated by source->ex. Android's etc1tool static const char pkmMagic[] = { 'P', 'K', 'M', ' ', '1', '0' }; const int pkmHeaderSize = 6 + 2 + 4 * 2; - const QByteArray header = f.read(pkmHeaderSize); + const QByteArray header = source->read(pkmHeaderSize); if (header.size() >= pkmHeaderSize && !qstrncmp(header.constData(), pkmMagic, 6)) { imageData = QTextureImageDataPtr::create(); imageData->setTarget(QOpenGLTexture::Target2D); @@ -455,7 +459,7 @@ QTextureImageDataPtr setPkmFile(const QString &source) imageData->setWidth(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2)))); imageData->setHeight(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2 + 2)))); imageData->setDepth(1); - const QByteArray data = f.readAll(); + const QByteArray data = source->readAll(); if (data.size() < (imageData->width() / 4) * (imageData->height() / 4) * 8) qWarning() << "Unexpected end of ETC1 data in" << source; const bool isCompressed = true; @@ -467,17 +471,12 @@ QTextureImageDataPtr setPkmFile(const QString &source) return imageData; } -QTextureImageDataPtr setDdsFile(const QString &source) +QTextureImageDataPtr setDdsFile(QIODevice *source) { QTextureImageDataPtr imageData; - QFile f(source); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << source; - return imageData; - } DdsHeader header; - if ((f.read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header) + if ((source->read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header) || (qstrncmp(header.magic, "DDS ", 4) != 0)) return imageData; @@ -490,7 +489,7 @@ QTextureImageDataPtr setDdsFile(const QString &source) if (fourCC == DdsFourCC<'D', 'X', '1', '0'>::value) { // DX10 texture DdsDX10Header dx10Header; - if (f.read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header) + if (source->read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header) return imageData; layers = qFromLittleEndian(dx10Header.arraySize); @@ -582,13 +581,13 @@ QTextureImageDataPtr setDdsFile(const QString &source) // data const int dataSize = layers * layerSize; - const QByteArray data = f.read(dataSize); + const QByteArray data = source->read(dataSize); if (data.size() < dataSize) { qWarning() << "Unexpected end of data in" << source; return imageData; } - if (!f.atEnd()) + if (!source->atEnd()) qWarning() << "Unrecognized data in" << source; imageData = QTextureImageDataPtr::create(); @@ -626,26 +625,39 @@ QTextureImageDataPtr TextureLoadingHelper::loadTextureData(const QUrl &url, bool #endif ) { const QString source = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(url); - const CompressedFormatExtension formatExtension = texturedCompressedFormat(source); - switch (formatExtension) { - case DDS: - textureData = setDdsFile(source); - break; - case PKM: - textureData = setPkmFile(source); - break; - default: - QImage img; - if (img.load(source)) { - textureData = QTextureImageDataPtr::create(); - textureData->setImage(mirrored ? img.mirrored() : img); - } - break; - } + QFile f(source); + if (!f.open(QIODevice::ReadOnly)) + qWarning() << "Failed to open" << source; + else + textureData = loadTextureData(&f, QFileInfo(source).suffix(), allow3D, mirrored); + } + return textureData; +} - if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1)) - qWarning() << "Texture data has a 3rd dimension which wasn't expected"; +QTextureImageDataPtr TextureLoadingHelper::loadTextureData(QIODevice *data, const QString& suffix, + bool allow3D, bool mirrored) +{ + QTextureImageDataPtr textureData; + const CompressedFormatExtension formatExtension = texturedCompressedFormat(suffix); + switch (formatExtension) { + case DDS: + textureData = setDdsFile(data); + break; + case PKM: + textureData = setPkmFile(data); + break; + default: { + QImage img; + if (img.load(data, suffix.toLatin1())) { + textureData = QTextureImageDataPtr::create(); + textureData->setImage(mirrored ? img.mirrored() : img); + } + break; + } } + + if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1)) + qWarning() << "Texture data has a 3rd dimension which wasn't expected"; return textureData; } @@ -653,8 +665,43 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()() { QTextureDataPtr generatedData = QTextureDataPtr::create(); m_status = QAbstractTexture::Loading; + QTextureImageDataPtr textureData; + + if (!Qt3DCore::QDownloadHelperService::isLocal(m_url)) { + if (m_sourceData.isEmpty()) { + // first time around, trigger a download + if (m_texture) { + auto downloadService = Qt3DCore::QDownloadHelperService::getService(m_engine); + Qt3DCore::QDownloadRequestPtr request(new TextureDownloadRequest(m_texture, m_url, + m_engine)); + downloadService->submitRequest(request); + } + return generatedData; + } + + // second time around, we have the data + QT_PREPEND_NAMESPACE(QBuffer) buffer(&m_sourceData); + if (buffer.open(QIODevice::ReadOnly)) { + QString suffix = m_url.toString(); + suffix = suffix.right(suffix.length() - suffix.lastIndexOf('.')); + + QStringList ext(suffix); - const QTextureImageDataPtr textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored); + QMimeDatabase db; + QMimeType mtype = db.mimeTypeForData(m_sourceData); + if (mtype.isValid()) { + ext << mtype.suffixes(); + } + + for (QString s: qAsConst(ext)) { + textureData = TextureLoadingHelper::loadTextureData(&buffer, suffix, true, m_mirrored); + if (textureData && textureData->data().length() > 0) + break; + } + } + } else { + textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored); + } if (textureData && textureData->data().length() > 0) { generatedData->setTarget(static_cast<QAbstractTexture::Target>(textureData->target())); @@ -672,12 +719,55 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()() return generatedData; } +TextureDownloadRequest::TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl& source, + Qt3DCore::QAspectEngine *engine) + : Qt3DCore::QDownloadRequest(source) + , m_texture(texture) + , m_engine(engine) +{ + +} + +// Executed in download thread +void TextureDownloadRequest::onCompleted() +{ + if (cancelled() || !succeeded()) + return; + + QRenderAspectPrivate* d_aspect = QRenderAspectPrivate::findPrivate(m_engine); + if (!d_aspect) + return; + + Render::Texture *texture = d_aspect->m_nodeManagers->textureManager()->lookupResource(m_texture); + if (!texture) + return; + + QSharedPointer<QTextureFromSourceGenerator> functor = + qSharedPointerCast<QTextureFromSourceGenerator>(texture->dataGenerator()); + functor->m_sourceData = m_data; + + // mark the component as dirty so that the functor runs again in the correct job + texture->addDirtyFlag(Render::Texture::DirtyDataGenerator); +} + QTextureLoaderPrivate::QTextureLoaderPrivate() : QAbstractTexturePrivate() , m_mirrored(true) { } +void QTextureLoaderPrivate::setScene(Qt3DCore::QScene *scene) +{ + QAbstractTexturePrivate::setScene(scene); + updateFunctor(); +} + +void QTextureLoaderPrivate::updateFunctor() +{ + Qt3DCore::QAspectEngine *engine = m_scene ? m_scene->engine() : nullptr; + setDataFunctor(QTextureFromSourceGeneratorPtr::create(m_id, m_source, m_mirrored, engine)); +} + /*! \class Qt3DRender::QTexture1D \inmodule Qt3DRender @@ -951,7 +1041,7 @@ void QTextureLoader::setSource(const QUrl& source) Q_D(QTextureLoader); if (source != d->m_source) { d->m_source = source; - d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored)); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit sourceChanged(source); blockNotifications(blocked); @@ -999,7 +1089,7 @@ void QTextureLoader::setMirrored(bool mirrored) Q_D(QTextureLoader); if (mirrored != d->m_mirrored) { d->m_mirrored = mirrored; - d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored)); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit mirroredChanged(mirrored); blockNotifications(blocked); @@ -1015,7 +1105,8 @@ bool QTextureFromSourceGenerator::operator ==(const QTextureGenerator &other) co const QTextureFromSourceGenerator *otherFunctor = functor_cast<QTextureFromSourceGenerator>(&other); return (otherFunctor != nullptr && otherFunctor->m_url == m_url && - otherFunctor->m_mirrored == m_mirrored); + otherFunctor->m_mirrored == m_mirrored && + otherFunctor->m_engine == m_engine); } QUrl QTextureFromSourceGenerator::url() const @@ -1033,16 +1124,18 @@ bool QTextureFromSourceGenerator::isMirrored() const * instance with \a url. * \param url */ -QTextureFromSourceGenerator::QTextureFromSourceGenerator(const QUrl &url, bool mirrored) +QTextureFromSourceGenerator::QTextureFromSourceGenerator(Qt3DCore::QNodeId texture, + const QUrl &url, bool mirrored, + Qt3DCore::QAspectEngine *engine) : QTextureGenerator() , m_url(url) , m_status(QAbstractTexture::None) , m_mirrored(mirrored) + , m_texture(texture) + , m_engine(engine) { } } // namespace Qt3DRender QT_END_NAMESPACE - - diff --git a/src/render/texture/qtexture_p.h b/src/render/texture/qtexture_p.h index a0ea71a58..4afb14d62 100644 --- a/src/render/texture/qtexture_p.h +++ b/src/render/texture/qtexture_p.h @@ -51,8 +51,11 @@ // We mean it. // +#include <Qt3DCore/QNodeId> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> #include <Qt3DRender/private/qabstracttexture_p.h> #include <Qt3DRender/qtexturegenerator.h> +#include <Qt3DRender/qtexture.h> QT_BEGIN_NAMESPACE @@ -63,14 +66,30 @@ class QTextureLoaderPrivate : public QAbstractTexturePrivate public: QTextureLoaderPrivate(); + void setScene(Qt3DCore::QScene *scene) override; + void updateFunctor(); + QUrl m_source; bool m_mirrored; }; +class Q_AUTOTEST_EXPORT TextureDownloadRequest : public Qt3DCore::QDownloadRequest +{ +public: + TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl &url, Qt3DCore::QAspectEngine *engine); + + void onCompleted() Q_DECL_OVERRIDE; + +private: + Qt3DCore::QNodeId m_texture; + Qt3DCore::QAspectEngine *m_engine; +}; + class Q_AUTOTEST_EXPORT QTextureFromSourceGenerator : public QTextureGenerator { public: - explicit QTextureFromSourceGenerator(const QUrl &url, bool mirrored); + explicit QTextureFromSourceGenerator(Qt3DCore::QNodeId texture, const QUrl &url, + bool mirrored, Qt3DCore::QAspectEngine *engine); QTextureDataPtr operator ()() Q_DECL_OVERRIDE; bool operator ==(const QTextureGenerator &other) const Q_DECL_OVERRIDE; inline QAbstractTexture::Status status() const { return m_status; } @@ -81,9 +100,14 @@ public: bool isMirrored() const; private: + friend class TextureDownloadRequest; + QUrl m_url; QAbstractTexture::Status m_status; bool m_mirrored; + QByteArray m_sourceData; + Qt3DCore::QNodeId m_texture; + Qt3DCore::QAspectEngine *m_engine; }; typedef QSharedPointer<QTextureFromSourceGenerator> QTextureFromSourceGeneratorPtr; @@ -91,6 +115,8 @@ class Q_AUTOTEST_EXPORT TextureLoadingHelper { public: static QTextureImageDataPtr loadTextureData(const QUrl &source, bool allow3D, bool mirrored); + static QTextureImageDataPtr loadTextureData(QIODevice *data, const QString& suffix, + bool allow3D, bool mirrored); }; } // namespace Qt3DRender diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp index 13991ec4a..21f29d0b6 100644 --- a/src/render/texture/texture.cpp +++ b/src/render/texture/texture.cpp @@ -81,11 +81,19 @@ void Texture::setTextureImageManager(TextureImageManager *manager) void Texture::addDirtyFlag(DirtyFlags flags) { + QMutexLocker lock(&m_flagsMutex); m_dirty |= flags; } +Texture::DirtyFlags Texture::dirtyFlags() +{ + QMutexLocker lock(&m_flagsMutex); + return m_dirty; +} + void Texture::unsetDirty() { + QMutexLocker lock(&m_flagsMutex); m_dirty = Texture::NotDirty; } diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h index 4fe4e2c7c..1f3ba729c 100644 --- a/src/render/texture/texture_p.h +++ b/src/render/texture/texture_p.h @@ -143,7 +143,7 @@ public: void setTextureImageManager(TextureImageManager *manager); void addDirtyFlag(DirtyFlags flags); - inline DirtyFlags dirtyFlags() const { return m_dirty; } + DirtyFlags dirtyFlags(); void unsetDirty(); void addTextureImage(Qt3DCore::QNodeId id); @@ -169,6 +169,7 @@ private: QVector<HTextureImage> m_textureImages; TextureImageManager *m_textureImageManager; + QMutex m_flagsMutex; }; class TextureFunctor : public Qt3DCore::QBackendNodeMapper diff --git a/tests/auto/render/qmesh/tst_qmesh.cpp b/tests/auto/render/qmesh/tst_qmesh.cpp index 2122d7eb5..a0278f4e4 100644 --- a/tests/auto/render/qmesh/tst_qmesh.cpp +++ b/tests/auto/render/qmesh/tst_qmesh.cpp @@ -121,7 +121,7 @@ private Q_SLOTS: const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QGeometryRendererData>>(creationChanges.first()); const Qt3DRender::QGeometryRendererData cloneData = creationChangeData->data; - Qt3DRender::MeshFunctor meshFunctor(mesh.source(), mesh.meshName()); + Qt3DRender::MeshLoaderFunctor meshFunctor(&mesh, nullptr); QVERIFY(meshFunctor == *cloneData.geometryFactory); QCOMPARE(mesh.id(), creationChangeData->subjectId()); @@ -169,7 +169,7 @@ private Q_SLOTS: QCOMPARE(change->propertyName(), "geometryFactory"); QCOMPARE(change->type(), Qt3DCore::PropertyUpdated); - Qt3DRender::MeshFunctor meshFunctor(mesh.source()); + Qt3DRender::MeshLoaderFunctor meshFunctor(&mesh, nullptr); Qt3DRender::QGeometryFactoryPtr factory = change->value().value<Qt3DRender::QGeometryFactoryPtr>(); QVERIFY(meshFunctor == *factory); @@ -205,7 +205,7 @@ private Q_SLOTS: QCOMPARE(change->propertyName(), "geometryFactory"); QCOMPARE(change->type(), Qt3DCore::PropertyUpdated); - Qt3DRender::MeshFunctor meshFunctor(QUrl(), mesh.meshName()); + Qt3DRender::MeshLoaderFunctor meshFunctor(&mesh, nullptr); Qt3DRender::QGeometryFactoryPtr factory = change->value().value<Qt3DRender::QGeometryFactoryPtr>(); QVERIFY(meshFunctor == *factory); diff --git a/tests/manual/custom-mesh-update-data-cpp/main.cpp b/tests/manual/custom-mesh-update-data-cpp/main.cpp index c7f5742dc..80ee2088d 100644 --- a/tests/manual/custom-mesh-update-data-cpp/main.cpp +++ b/tests/manual/custom-mesh-update-data-cpp/main.cpp @@ -277,7 +277,7 @@ int main(int argc, char* argv[]) void TimerObject::timeout() { - angle += float(M_PI / 360.0); + angle += qDegreesToRadians(0.5f); QByteArray updateData; updateData.resize(3*sizeof(float)); diff --git a/tests/manual/downloading/downloading.pro b/tests/manual/downloading/downloading.pro new file mode 100644 index 000000000..6f1b6e3d5 --- /dev/null +++ b/tests/manual/downloading/downloading.pro @@ -0,0 +1,14 @@ +!include( ../manual.pri ) { + error( "Couldn't find the manual.pri file!" ) +} + +QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras + +SOURCES += \ + main.cpp + +OTHER_FILES += \ + main.qml + +RESOURCES += \ + downloading.qrc diff --git a/tests/manual/downloading/downloading.qrc b/tests/manual/downloading/downloading.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/downloading/downloading.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/downloading/main.cpp b/tests/manual/downloading/main.cpp new file mode 100644 index 000000000..dba6e0bff --- /dev/null +++ b/tests/manual/downloading/main.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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 <Qt3DQuickExtras/qt3dquickwindow.h> +#include <QGuiApplication> + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc, argv); + Qt3DExtras::Quick::Qt3DQuickWindow view; + + view.setSource(QUrl("qrc:/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/tests/manual/downloading/main.qml b/tests/manual/downloading/main.qml new file mode 100644 index 000000000..e2f42284d --- /dev/null +++ b/tests/manual/downloading/main.qml @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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$ +** +****************************************************************************/ + +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Input 2.0 +import Qt3D.Extras 2.0 + +Entity { + id: sceneRoot + + Camera { + id: camera + position: Qt.vector3d( 0.0, 0.5, 15.0 ) + viewCenter: Qt.vector3d( 0.0, 0.5, 0.0 ) + } + + OrbitCameraController { camera: camera } + + RenderSettings { + id : external_forward_renderer + activeFrameGraph : ForwardRenderer { + camera: camera + clearColor: "white" + } + } + + // Event Source will be set by the Qt3DQuickWindow + InputSettings { id: inputSettings } + + DirectionalLight { id: light; worldDirection: camera.viewVector } + + components: [external_forward_renderer, inputSettings, light] + + Mesh { + id: mesh + source: "https://codereview.qt-project.org/gitweb?p=qt/qt3d.git;a=blob_plain;hb=refs/heads/dev;f=examples/qt3d/exampleresources/assets/chest/Chest.obj" + } + + Transform { + id: transform + scale: 0.03 + rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 30) + } + + DiffuseMapMaterial { + id: material + diffuse: TextureLoader { source: "https://codereview.qt-project.org/gitweb?p=qt/qt3d.git;a=blob_plain;hb=refs/heads/dev;f=examples/qt3d/planets-qml/images/earthmap1k.jpg" } + specular: Qt.rgba( 0.2, 0.2, 0.2, 1.0 ) + shininess: 2.0 + } + + Entity { + id: mainEntity + objectName: "mainEntity" + components: [ mesh, material, transform ] + } +} diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index fa7b016f9..1dd418500 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -16,6 +16,7 @@ SUBDIRS += \ cylinder-qml \ deferred-renderer-cpp \ deferred-renderer-qml \ + downloading \ dragging \ dynamicscene-cpp \ enabled-qml \ |