diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2015-01-21 15:23:39 +0100 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2015-01-24 14:30:24 +0100 |
commit | eeab3dabf1b374867377128330c302228759bb5f (patch) | |
tree | fd7620fd743a0a57bf7f2f8dff9a9c756c65f100 | |
parent | 7bc827504190858bccdff82f63211f4988bc48df (diff) |
RenderShaderData: ModelToEye/ModelToWorld property transform
* Reuse jobs in QRenderAspect
* Added FramePreparationJob which:
- updates RenderShaderData with node world matrix
- create bounding volume for nodes
* RenderShaderData slightly modified to accommodate the transformed properties
- needsUpdate now takes the viewMatrix
- fix a bug where for a nested array, order wasn't maintained if not all the
elements had been updated
Change-Id: I0d91dc1d52c4333be74cce56c334aea70498138e
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r-- | src/render/backend/jobs/framepreparationjob.cpp | 105 | ||||
-rw-r--r-- | src/render/backend/jobs/framepreparationjob_p.h | 81 | ||||
-rw-r--r-- | src/render/backend/jobs/render-jobs.pri | 6 | ||||
-rw-r--r-- | src/render/backend/jobs/renderviewjob.cpp | 4 | ||||
-rw-r--r-- | src/render/backend/jobs/renderviewjobutils.cpp | 52 | ||||
-rw-r--r-- | src/render/backend/jobs/renderviewjobutils_p.h | 2 | ||||
-rw-r--r-- | src/render/backend/qrenderaspect.cpp | 23 | ||||
-rw-r--r-- | src/render/backend/qrenderaspect_p.h | 9 | ||||
-rw-r--r-- | src/render/backend/rendermesh.cpp | 7 | ||||
-rw-r--r-- | src/render/backend/rendermesh_p.h | 3 | ||||
-rw-r--r-- | src/render/backend/rendershaderdata.cpp | 88 | ||||
-rw-r--r-- | src/render/backend/rendershaderdata_p.h | 15 | ||||
-rw-r--r-- | src/render/backend/renderview.cpp | 17 | ||||
-rw-r--r-- | src/render/backend/renderview_p.h | 5 | ||||
-rw-r--r-- | src/render/frontend/sphere.h | 2 |
15 files changed, 331 insertions, 88 deletions
diff --git a/src/render/backend/jobs/framepreparationjob.cpp b/src/render/backend/jobs/framepreparationjob.cpp new file mode 100644 index 000000000..fff252a9b --- /dev/null +++ b/src/render/backend/jobs/framepreparationjob.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "framepreparationjob_p.h" +#include <Qt3DRenderer/private/renderer_p.h> +#include <Qt3DRenderer/private/renderentity_p.h> +#include <Qt3DRenderer/private/rendermesh_p.h> +#include <Qt3DRenderer/private/rendershaderdata_p.h> +#include <Qt3DRenderer/qmeshdata.h> +#include <Qt3DRenderer/sphere.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +namespace Render { + +FramePreparationJob::FramePreparationJob(Renderer *renderer, RenderEntity *root) + : m_renderer(renderer) + , m_root(root) +{ +} + +FramePreparationJob::~FramePreparationJob() +{ + +} + +void FramePreparationJob::run() +{ + parseNodeTree(m_root); +} + +void FramePreparationJob::parseNodeTree(RenderEntity *node) +{ + // Initialize worldBoundingVolume if Mesh associated + Qt3D::Render::RenderMesh *mesh = Q_NULLPTR; + if ((node->localBoundingVolume()->isNull()) + && (mesh = node->renderComponent<RenderMesh>()) != Q_NULLPTR) { + if (!mesh->meshDataHandle().isNull()) { + Qt3D::QMeshData *meshData = mesh->meshData(); + if (meshData != Q_NULLPTR) { + const QAxisAlignedBoundingBox box = meshData->boundingBox(); + node->localBoundingVolume()->setCenter(box.center()); + const QVector3D &radii = box.radii(); + node->localBoundingVolume()->setRadius(qMax(radii.x(), qMax(radii.y(), radii.z()))); + qDebug() << node->localBoundingVolume()->center() << node->localBoundingVolume()->radius(); + } + } + } + + // Update transform properties in RenderShaderData + QList<RenderShaderData *> shadersData = node->renderComponents<RenderShaderData>(); + Q_FOREACH (RenderShaderData *r, shadersData) { + r->updateTransformedProperties(*node->worldTransform()); + } + + // Traverse children + Q_FOREACH (RenderEntity *child, node->children()) + parseNodeTree(child); +} + +} // Render + +} // Qt3D + +QT_END_NAMESPACE diff --git a/src/render/backend/jobs/framepreparationjob_p.h b/src/render/backend/jobs/framepreparationjob_p.h new file mode 100644 index 000000000..63513b82e --- /dev/null +++ b/src/render/backend/jobs/framepreparationjob_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_RENDER_FRAMEPREPARATIONJOB_H +#define QT3D_RENDER_FRAMEPREPARATIONJOB_H + +#include <Qt3DCore/qaspectjob.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +namespace Render { + +class Renderer; +class RenderEntity; + +class FramePreparationJob : public Qt3D::QAspectJob +{ +public: + FramePreparationJob(Renderer *renderer, RenderEntity *root); + ~FramePreparationJob(); + +protected: + void run() Q_DECL_FINAL; + +private: + + void parseNodeTree(RenderEntity *node); + + Renderer *m_renderer; + RenderEntity *m_root; +}; + +typedef QSharedPointer<FramePreparationJob> FramePreparationJobPtr; + +} // Render + +} // Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_RENDER_FRAMEPREPARATIONJOB_H diff --git a/src/render/backend/jobs/render-jobs.pri b/src/render/backend/jobs/render-jobs.pri index 304ed0d53..4b1b750fc 100644 --- a/src/render/backend/jobs/render-jobs.pri +++ b/src/render/backend/jobs/render-jobs.pri @@ -7,7 +7,8 @@ HEADERS += \ $$PWD/renderviewjob_p.h \ $$PWD/renderviewjobutils_p.h \ $$PWD/loadscenejob_p.h \ - $$PWD/framecleanupjob_p.h + $$PWD/framecleanupjob_p.h \ + $$PWD/framepreparationjob_p.h SOURCES += \ $$PWD/updateworldtransformjob.cpp \ @@ -16,4 +17,5 @@ SOURCES += \ $$PWD/renderviewjob.cpp \ $$PWD/renderviewjobutils.cpp \ $$PWD/loadscenejob.cpp \ - $$PWD/framecleanupjob.cpp + $$PWD/framecleanupjob.cpp \ + $$PWD/framepreparationjob.cpp diff --git a/src/render/backend/jobs/renderviewjob.cpp b/src/render/backend/jobs/renderviewjob.cpp index 6f1344616..565b0f67e 100644 --- a/src/render/backend/jobs/renderviewjob.cpp +++ b/src/render/backend/jobs/renderviewjob.cpp @@ -70,10 +70,6 @@ void RenderViewJob::run() // Populate the renderview's configuration from the framegraph setRenderViewConfigFromFrameGraphLeafNode(renderView, m_fgLeaf); - // Gather resources needed for buildRenderCommand - // Ex lights, we need all lights in the scene before we can buildRenderCommands - preprocessRenderTree(renderView, m_renderer->renderSceneRoot()); - // Build RenderCommand should perform the culling as we have no way to determine // if a child has a mesh in the view frustrum while its parent isn't contained in it. renderView->buildRenderCommands(m_renderer->renderSceneRoot()); diff --git a/src/render/backend/jobs/renderviewjobutils.cpp b/src/render/backend/jobs/renderviewjobutils.cpp index 74d0a8a76..52f20ab03 100644 --- a/src/render/backend/jobs/renderviewjobutils.cpp +++ b/src/render/backend/jobs/renderviewjobutils.cpp @@ -188,58 +188,6 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN /*! \internal - Walks the scene graph of RenderEntities rooted at \p node and collects - together any resources needed by the \p rv. -*/ -void preprocessRenderTree(RenderView *rv, const RenderEntity *node) -{ - // The goal is to use QShaderData in a later revision - - // Note : Layer filtering isn't applied there - // TODO: Perhaps make this block of code configurable by allowing the Technique - // or similar to provide a functor? - - // For each of entity that has a QShaderData component we need to save the worldTransform so that we can - // later use the shaderData with the correct space transforms - - // COMMENTED FOR NOW AS THERE ARE ISSUES WITH MULTIPLE RENDERVIEWS - // AS THE TRANSFORMS ARE COMPUTED BASED ON THE RENDERVIEWS' VIEWMATRIX - // AND SINCE THERE CAN BE SEVERAL RENDERVIEWS FOR A SINGLE QSHADERDATA - // THIS RESULT IN TWO JOBS UPDATING THE RENDERSHADERDATA AT THE SAME TIME - // Moving that to the RenderView::setUniformBlock could solve that - - // QList<RenderShaderData *> shadersData = node->renderComponents<RenderShaderData>(); - // Q_FOREACH (RenderShaderData *r, shadersData) { - // if (r) { - // QHash<QString, QVariant> &shaderProperties = r->properties(); - // QHash<QString, QVariant>::iterator it = shaderProperties.begin(); - // const QHash<QString, QVariant>::iterator itEnd = shaderProperties.end(); - - // while (it != itEnd) { - // if (static_cast<QMetaType::Type>(it.value().type()) == QMetaType::QVector3D) { - // // If we have a QVector3D property value, we try to look - // // if there is a matching QShaderData::TransformType propertyTransformed - // QVariant value = shaderProperties.value(it.key() + QStringLiteral("Transformed")); - // // if that's the case, we apply a space transformation to the property - // if (value.isValid() && value.type() == QVariant::Int) { - // if (static_cast<QShaderData::TransformType>(value.toInt()) == QShaderData::ModelToEye) - // it.value() = QVariant(rv->viewmatrix() * *node->worldTransform() * it.value().value<QVector3D>()); - // else // ModelToWorld - // it.value() = QVariant(*node->worldTransform() * it.value().value<QVector3D>()); - // } - // } - // ++it; - // } - // } - // } - - // Traverse children - Q_FOREACH (RenderEntity *child, node->children()) - preprocessRenderTree(rv, child); -} - -/*! - \internal Searches the \a renderer for the best matching RenderTechnique from \a effect specified by the \a renderView. */ diff --git a/src/render/backend/jobs/renderviewjobutils_p.h b/src/render/backend/jobs/renderviewjobutils_p.h index 25ee25033..b54ab0733 100644 --- a/src/render/backend/jobs/renderviewjobutils_p.h +++ b/src/render/backend/jobs/renderviewjobutils_p.h @@ -69,8 +69,6 @@ class Renderer; Q_AUTOTEST_EXPORT void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf); -Q_AUTOTEST_EXPORT void preprocessRenderTree(RenderView *rv, const RenderEntity *node); - Q_AUTOTEST_EXPORT RenderTechnique *findTechniqueForEffect(Renderer *renderer, RenderView *renderView, RenderEffect *effect); diff --git a/src/render/backend/qrenderaspect.cpp b/src/render/backend/qrenderaspect.cpp index e8ab17f0a..787d7d6c0 100644 --- a/src/render/backend/qrenderaspect.cpp +++ b/src/render/backend/qrenderaspect.cpp @@ -234,6 +234,11 @@ QVector<QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time) // Create jobs to load in any meshes that are pending if (d->m_renderer != Q_NULLPTR) { + d->m_framePreparationJob.reset(new Render::FramePreparationJob(d->m_renderer, d->m_renderer->renderSceneRoot())); + d->m_cleanupJob.reset(new Render::FrameCleanupJob(d->m_renderer)); + d->m_worldTransformJob.reset(new Render::UpdateWorldTransformJob(d->m_renderer->renderSceneRoot())); + d->m_boundingVolumeJob.reset(new Render::UpdateBoundingVolumeJob(d->m_renderer->renderSceneRoot())); + QHash<QNodeId, QAbstractMeshFunctorPtr> meshSources = d->m_renderer->meshDataManager()->meshesPending(); Q_FOREACH (const QNodeId &meshId, meshSources.keys()) { Render::LoadMeshDataJobPtr loadMeshJob(new Render::LoadMeshDataJob(meshSources[meshId], meshId)); @@ -251,28 +256,26 @@ QVector<QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time) } // Create jobs to update transforms and bounding volumes - Render::UpdateWorldTransformJobPtr worldTransformJob(new Render::UpdateWorldTransformJob(d->m_renderer->renderSceneRoot())); - Render::UpdateBoundingVolumeJobPtr boundingVolumeJob(new Render::UpdateBoundingVolumeJob(d->m_renderer->renderSceneRoot())); - // We can only update bounding volumes once all world transforms are known - boundingVolumeJob->addDependency(worldTransformJob); + d->m_boundingVolumeJob->addDependency(d->m_worldTransformJob); + d->m_framePreparationJob->addDependency(d->m_worldTransformJob); // Add all jobs to queue - jobs.append(worldTransformJob); - jobs.append(boundingVolumeJob); + jobs.append(d->m_worldTransformJob); + jobs.append(d->m_boundingVolumeJob); + jobs.append(d->m_framePreparationJob); // Traverse the current framegraph and create jobs to populate // RenderBins with RenderCommands QVector<QAspectJobPtr> renderBinJobs = d->m_renderer->createRenderBinJobs(); // TODO: Add wrapper around ThreadWeaver::Collection - Render::FrameCleanupJobPtr cleanupJob(new Render::FrameCleanupJob(d->m_renderer)); for (int i = 0; i < renderBinJobs.size(); ++i) { QAspectJobPtr renderBinJob = renderBinJobs.at(i); - renderBinJob->addDependency(boundingVolumeJob); + renderBinJob->addDependency(d->m_boundingVolumeJob); jobs.append(renderBinJob); - cleanupJob->addDependency(renderBinJob); + d->m_cleanupJob->addDependency(renderBinJob); } - jobs.append(cleanupJob); + jobs.append(d->m_cleanupJob); } return jobs; } diff --git a/src/render/backend/qrenderaspect_p.h b/src/render/backend/qrenderaspect_p.h index 6320bfd09..b6e83f093 100644 --- a/src/render/backend/qrenderaspect_p.h +++ b/src/render/backend/qrenderaspect_p.h @@ -44,7 +44,10 @@ #include <Qt3DCore/private/qabstractaspect_p.h> #include <Qt3DRenderer/qrenderaspect.h> - +#include <Qt3DRenderer/private/updateboundingvolumejob_p.h> +#include <Qt3DRenderer/private/updateworldtransformjob_p.h> +#include <Qt3DRenderer/private/framepreparationjob_p.h> +#include <Qt3DRenderer/private/framecleanupjob_p.h> #include <Qt3DRenderer/private/platformsurfacefilter_p.h> QT_BEGIN_NAMESPACE @@ -74,6 +77,10 @@ class QRenderAspectPrivate : public QAbstractAspectPrivate qint64 m_time; bool m_initialized; + Render::FramePreparationJobPtr m_framePreparationJob; + Render::FrameCleanupJobPtr m_cleanupJob; + Render::UpdateWorldTransformJobPtr m_worldTransformJob; + Render::UpdateBoundingVolumeJobPtr m_boundingVolumeJob; }; } diff --git a/src/render/backend/rendermesh.cpp b/src/render/backend/rendermesh.cpp index bf5fd49c0..e99f8c23c 100644 --- a/src/render/backend/rendermesh.cpp +++ b/src/render/backend/rendermesh.cpp @@ -112,7 +112,12 @@ void RenderMesh::sceneChangeEvent(const QSceneChangePtr &e) } } -HMeshData RenderMesh::meshData() const +QMeshData *RenderMesh::meshData() const +{ + return m_meshDataManager->data(m_meshDataHandle); +} + +HMeshData RenderMesh::meshDataHandle() const { return m_meshDataHandle; } diff --git a/src/render/backend/rendermesh_p.h b/src/render/backend/rendermesh_p.h index c7563d0af..1f2451968 100644 --- a/src/render/backend/rendermesh_p.h +++ b/src/render/backend/rendermesh_p.h @@ -77,7 +77,8 @@ public: void updateFromPeer(QNode *peer) Q_DECL_OVERRIDE; void sceneChangeEvent(const QSceneChangePtr &e) Q_DECL_OVERRIDE; - HMeshData meshData() const; + HMeshData meshDataHandle() const; + QMeshData *meshData() const; void setMeshData(HMeshData handle); void setMeshDataManager(MeshDataManager *manager); diff --git a/src/render/backend/rendershaderdata.cpp b/src/render/backend/rendershaderdata.cpp index 0466f6e67..b8f78aeb5 100644 --- a/src/render/backend/rendershaderdata.cpp +++ b/src/render/backend/rendershaderdata.cpp @@ -103,11 +103,37 @@ void RenderShaderData::clearUpdate() } // Called by renderview jobs (several concurrent threads) -bool RenderShaderData::needsUpdate() +/*! + \internal + Lookup if the current ShaderData or a nested ShaderData has updated properties. + UpdateProperties contains either the value of the propertie of a QNodeId if it's another ShaderData. + Transformed properties are updated for all of ShaderData that have ones at the point. + + \note This needs to be performed for every top level RenderShaderData every time it is used. + As we don't know if the transformed properties use the same viewMatrix for all RenderViews. + */ +bool RenderShaderData::needsUpdate(const QMatrix4x4 &viewMatrix) { // We can't perform this only once as we don't know if we would be call as the root or a // nested RenderShaderData QMutexLocker lock(m_mutex); + + // Update transformed properties + // We check the matrices and decide if the transform has changed since the previous call to needsUpdate + if (m_viewMatrix != viewMatrix) { + m_viewMatrix = viewMatrix; + const QHash<QString, QShaderData::TransformType>::const_iterator transformedEnd = m_transformedProperties.end(); + QHash<QString, QShaderData::TransformType>::const_iterator transformedIt = m_transformedProperties.begin(); + + while (transformedIt != transformedEnd) { + if (transformedIt.value() == QShaderData::ModelToEye) { + m_updatedProperties.insert(transformedIt.key(), m_viewMatrix * m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + m_properties.insert(transformedIt.key(), m_viewMatrix * m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + } + ++transformedIt; + } + } + const QHash<QString, QVariant>::const_iterator end = m_nestedShaderDataProperties.end(); QHash<QString, QVariant>::const_iterator it = m_nestedShaderDataProperties.begin(); @@ -116,14 +142,20 @@ bool RenderShaderData::needsUpdate() QVariantList updatedNodes; Q_FOREACH (const QVariant &v, it.value().value<QVariantList>()) { RenderShaderData *nested = m_manager->lookupResource(v.value<QNodeId>()); - if (nested != Q_NULLPTR && nested->needsUpdate()) + if (nested != Q_NULLPTR) { + // We need to add the nested nodes the the updated property list + // as if we need to maintain order + // if node[0] doesn't need update but node[1] does, + // if we only have a single element, the renderer would update element [0] + nested->needsUpdate(viewMatrix); updatedNodes << v; + } } if (!updatedNodes.empty()) m_updatedProperties.insert(it.key(), updatedNodes); } else { RenderShaderData *nested = m_manager->lookupResource(it.value().value<QNodeId>()); - if (nested != Q_NULLPTR && nested->needsUpdate()) + if (nested != Q_NULLPTR && nested->needsUpdate(viewMatrix)) m_updatedProperties.insert(it.key(), it.value()); } ++it; @@ -131,6 +163,27 @@ bool RenderShaderData::needsUpdate() return m_updatedProperties.size() > 0; } +void RenderShaderData::updateTransformedProperties(const QMatrix4x4 &nodeWorldMatrix) +{ + if (m_worldMatrix != nodeWorldMatrix) { + m_worldMatrix = nodeWorldMatrix; + + const QHash<QString, QShaderData::TransformType>::const_iterator transformedEnd = m_transformedProperties.end(); + QHash<QString, QShaderData::TransformType>::const_iterator transformedIt = m_transformedProperties.begin(); + + while (transformedIt != transformedEnd) { + if (transformedIt.value() == QShaderData::ModelToEye) { + m_updatedProperties.insert(transformedIt.key(), m_viewMatrix * m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + m_properties.insert(transformedIt.key(), m_viewMatrix * m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + } else { + m_updatedProperties.insert(transformedIt.key(), m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + m_properties.insert(transformedIt.key(), m_worldMatrix * m_originalProperties.value(transformedIt.key()).value<QVector3D>()); + } + ++transformedIt; + } + } +} + // This will add the RenderShaderData to be cleared from updates at the end of the frame // by the cleanup job // Called by renderview jobs (several concurrent threads) @@ -157,6 +210,7 @@ void RenderShaderData::readPeerProperties(QShaderData *shaderData) QString propertyName = QString::fromLatin1(property.name()); m_properties.insert(propertyName, propertyValue); + m_originalProperties.insert(propertyName, propertyValue); // We check if the property is a QNodeId or QList<QNodeId> so that we can // check nested QShaderData for update @@ -168,6 +222,21 @@ void RenderShaderData::readPeerProperties(QShaderData *shaderData) m_nestedShaderDataProperties.insert(propertyName, propertyValue); } } + + // We look for transformed properties + QHash<QString, QVariant>::iterator it = m_properties.begin(); + const QHash<QString, QVariant>::iterator end = m_properties.end(); + + while (it != end) { + if (static_cast<QMetaType::Type>(it.value().type()) == QMetaType::QVector3D) { + // if there is a matching QShaderData::TransformType propertyTransformed + QVariant value = m_properties.value(it.key() + QStringLiteral("Transformed")); + // if that's the case, we apply a space transformation to the property + if (value.isValid() && value.type() == QVariant::Int) + m_transformedProperties.insert(it.key(), static_cast<QShaderData::TransformType>(value.toInt())); + } + ++it; + } } void RenderShaderData::setManager(ShaderDataManager *manager) @@ -185,7 +254,16 @@ void RenderShaderData::sceneChangeEvent(const QSceneChangePtr &e) // Note we aren't notified about nested QShaderData in this call // only scalar / vec properties if (m_properties.contains(propertyName)) { - const QVariant &val = m_propertyReader->readProperty(propertyChange->value()); + QVariant val = m_propertyReader->readProperty(propertyChange->value()); + // If this is a Transformed property, we need to multiply against the correct + // matrices + m_originalProperties.insert(propertyName, val); + if (m_transformedProperties.contains(propertyName)) { + if (m_transformedProperties[propertyName] == QShaderData::ModelToEye) + val = m_viewMatrix * m_worldMatrix * val.value<QVector3D>(); + else + val = m_worldMatrix * val.value<QVector3D>(); + } m_properties.insert(propertyName, val); m_updatedProperties.insert(propertyName, val); } @@ -193,11 +271,13 @@ void RenderShaderData::sceneChangeEvent(const QSceneChangePtr &e) } case NodeAdded: { m_properties.insert(propertyName, m_propertyReader->readProperty(propertyChange->value())); + m_originalProperties.insert(propertyName, m_propertyReader->readProperty(propertyChange->value())); m_nestedShaderDataProperties.insert(propertyName, propertyChange->value()); break; } case NodeRemoved: { if (m_properties.contains(propertyName)) { + m_originalProperties.remove(propertyName); m_properties.remove(propertyName); m_nestedShaderDataProperties.remove(propertyName); } diff --git a/src/render/backend/rendershaderdata_p.h b/src/render/backend/rendershaderdata_p.h index 79c4a9aeb..6e74117ac 100644 --- a/src/render/backend/rendershaderdata_p.h +++ b/src/render/backend/rendershaderdata_p.h @@ -46,6 +46,7 @@ #include <private/shadervariables_p.h> #include <Qt3DRenderer/qshaderdata.h> #include <QMutex> +#include <QMatrix4x4> QT_BEGIN_NAMESPACE @@ -64,7 +65,6 @@ public: ~RenderShaderData(); void updateFromPeer(QNode *peer) Q_DECL_OVERRIDE; - inline QHash<QString, QVariant> & properties() { return m_properties; } inline QHash<QString, QVariant> properties() const { return m_properties; } inline QHash<QString, QVariant> updatedProperties() const { return m_updatedProperties; } @@ -75,19 +75,30 @@ public: // Call by RenderViewJobs void addToClearUpdateList(); - bool needsUpdate(); + bool needsUpdate(const QMatrix4x4 &viewMatrix); + + void updateTransformedProperties(const QMatrix4x4 &nodeWordlTransform); protected: void sceneChangeEvent(const QSceneChangePtr &e) Q_DECL_OVERRIDE; private: + // 1 to 1 match with frontend properties, modified only by sceneChangeEvent + QHash<QString, QVariant> m_originalProperties; + // 1 to 1 match with frontend properties apart from Transformed + // properties which contain the matrices product QHash<QString, QVariant> m_properties; + // only updated properties, Transformed properties have the same + // value as in m_properties QHash<QString, QVariant> m_updatedProperties; PropertyReaderInterfacePtr m_propertyReader; QHash<QString, QVariant> m_nestedShaderDataProperties; + QHash<QString, QShaderData::TransformType> m_transformedProperties; ShaderDataManager *m_manager; QMutex *m_mutex; static QList<QNodeId> m_updatedShaderData; + QMatrix4x4 m_worldMatrix; + QMatrix4x4 m_viewMatrix; void readPeerProperties(QShaderData *peer); void setManager(ShaderDataManager *manager); diff --git a/src/render/backend/renderview.cpp b/src/render/backend/renderview.cpp index 0d62ab486..ae05da252 100644 --- a/src/render/backend/renderview.cpp +++ b/src/render/backend/renderview.cpp @@ -330,9 +330,8 @@ void RenderView::buildRenderCommands(RenderEntity *node) if (node->componentHandle<RenderMesh, 16>() != HMesh() && (mesh = node->renderComponent<RenderMesh>()) != Q_NULLPTR && mesh->isEnabled()) { - if (!mesh->meshData().isNull()) - { - // Perform culling here + if (!mesh->meshDataHandle().isNull()) { + // TO DO: Perform culling here // As shaders may be deforming, transforming the mesh // We might want to make that optional or dependent on an explicit bounding box item @@ -361,7 +360,7 @@ void RenderView::buildRenderCommands(RenderEntity *node) Q_FOREACH (RenderRenderPass *pass, passes) { RenderCommand *command = m_allocator->allocate<RenderCommand>(); command->m_depth = m_data->m_eyePos.distanceToPoint(node->worldBoundingVolume()->center()); - command->m_meshData = mesh->meshData(); + command->m_meshData = mesh->meshDataHandle(); command->m_instancesCount = 0; // TODO: Build the state set for a render pass only once per-pass. Not once per rendercommand and pass. @@ -496,13 +495,14 @@ void RenderView::setUniformBlockValue(QUniformPack &uniformPack, RenderShader *s // If rShaderData has been updated (property has changed or one of the nested properties has changed) // foreach property defined in the QShaderData, we try to fill the value of the corresponding active uniform(s) // for all the updated properties (all the properties if the UBO was just created) - if (rShaderData->needsUpdate() || uboNeedsUpdate) { + if (rShaderData->needsUpdate(*m_data->m_viewMatrix) || uboNeedsUpdate) { // Clear previous values remaining in the hash m_activeUniformNamesToValue.clear(); // Retrieve names and description of each active uniforms in the uniform block const QHash<QString, ShaderUniform> &activeProperties = shader->activeUniformsForBlock(block.m_index); - const QHash<QString, QVariant> &properties = uboNeedsUpdate ? rShaderData->properties() : rShaderData->updatedProperties(); + // We want a copy here in case another RenderViewJobs modifies the updatedProperties of the RenderShaderData + const QHash<QString, QVariant> properties = uboNeedsUpdate ? rShaderData->properties() : rShaderData->updatedProperties(); QHash<QString, QVariant>::const_iterator it = properties.begin(); const QHash<QString, QVariant>::const_iterator end = properties.end(); @@ -527,6 +527,8 @@ void RenderView::setDefaultUniformBlockShaderDataValue(QUniformPack &uniformPack RenderShaderData *rShaderData = m_renderer->shaderDataManager()->lookupResource(shaderData->id()); if (rShaderData) { + // updates transformed properties; + rShaderData->needsUpdate(*m_data->m_viewMatrix); // Retrieve names and description of each active uniforms in the uniform block const QHash<QString, ShaderUniform> &activeUniformsInDefaultBlock = shader->activeUniformsForBlock(-1); @@ -576,8 +578,7 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, RenderRenderPass * // For each ParameterBinder in the RenderPass -> create a QUniformPack // Once that works, improve that to try and minimize QUniformPack updates - if (rPass != Q_NULLPTR) - { + if (rPass != Q_NULLPTR) { // Index RenderShader by Shader UUID command->m_shader = m_renderer->shaderManager()->lookupHandle(rPass->shaderProgram()); RenderShader *shader = Q_NULLPTR; diff --git a/src/render/backend/renderview_p.h b/src/render/backend/renderview_p.h index 1fbc1d976..bc3401d68 100644 --- a/src/render/backend/renderview_p.h +++ b/src/render/backend/renderview_p.h @@ -253,7 +253,10 @@ private: QUniformValue *time(const QMatrix4x4 &model) const; void setUniformValue(QUniformPack &uniformPack, const QString &name, const QVariant &value); - void setUniformBlockValue(QUniformPack &uniformPack, RenderShader *shader, const ShaderUniformBlock &block, const QVariant &value); + void setUniformBlockValue(QUniformPack &uniformPack, + RenderShader *shader, + const ShaderUniformBlock &block, + const QVariant &value); void buildActiveUniformNameValueMap(const QHash<QString, ShaderUniform> &uniforms, const QString &blockName, const QString &qmlPropertyName, diff --git a/src/render/frontend/sphere.h b/src/render/frontend/sphere.h index 24b584367..252a5ec95 100644 --- a/src/render/frontend/sphere.h +++ b/src/render/frontend/sphere.h @@ -67,6 +67,8 @@ public: void setCenter(const QVector3D &c); QVector3D center() const; + inline bool isNull() { return m_center == QVector3D() && m_radius == 0.0f; } + void setRadius(float r); float radius() const; |