diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2018-02-05 13:01:09 +0000 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2018-02-05 15:42:29 +0000 |
commit | 0403ee45736241aa621eb3d38880a4fff571fd96 (patch) | |
tree | 94650145a3b7e2517dfcf29d33f97859933b518d | |
parent | 04afcf1cb9e79697360baa01a97a26815237eba1 (diff) | |
parent | ceae743678d41a58154612781e896c04c87a8c4f (diff) |
Merge remote-tracking branch 'origin/5.9' into 5.10
Conflicts:
.qmake.conf
src/render/backend/trianglesvisitor.cpp
src/render/backend/uniform.cpp
src/render/jobs/calcboundingvolumejob.cpp
src/render/jobs/pickboundingvolumejob.cpp
src/render/jobs/pickboundingvolumeutils.cpp
Change-Id: Ib8305011c51710a3538c0b29f7022388f5244a38
42 files changed, 1260 insertions, 252 deletions
diff --git a/src/core/changes/qcomponentaddedchange.cpp b/src/core/changes/qcomponentaddedchange.cpp index e00c4ffab..41f7a2340 100644 --- a/src/core/changes/qcomponentaddedchange.cpp +++ b/src/core/changes/qcomponentaddedchange.cpp @@ -75,7 +75,7 @@ QComponentAddedChangePrivate::QComponentAddedChangePrivate(const QEntity *entity */ /*! - * Constructs a new QComponentAddedChange with with \a entity and \a component. + * Constructs a new QComponentAddedChange which will notify \a entity that \a component was added */ QComponentAddedChange::QComponentAddedChange(const QEntity *entity, const QComponent *component) @@ -84,6 +84,16 @@ QComponentAddedChange::QComponentAddedChange(const QEntity *entity, { } +/*! + * Constructs a new QComponentAddedChange which will notify \a component that it was added to \a entity + */ +QComponentAddedChange::QComponentAddedChange(const QComponent *component, + const QEntity *entity) + : QSceneChange(*new QComponentAddedChangePrivate(entity, component), + ComponentAdded, component->id()) +{ +} + QComponentAddedChange::~QComponentAddedChange() { } diff --git a/src/core/changes/qcomponentaddedchange.h b/src/core/changes/qcomponentaddedchange.h index a62cac116..e7676a026 100644 --- a/src/core/changes/qcomponentaddedchange.h +++ b/src/core/changes/qcomponentaddedchange.h @@ -55,6 +55,8 @@ class QT3DCORESHARED_EXPORT QComponentAddedChange : public QSceneChange public: explicit QComponentAddedChange(const QEntity *entity, const QComponent *component); + explicit QComponentAddedChange(const QComponent *component, + const QEntity *entity); ~QComponentAddedChange(); QNodeId entityId() const Q_DECL_NOTHROW; diff --git a/src/core/changes/qcomponentremovedchange.cpp b/src/core/changes/qcomponentremovedchange.cpp index 5e5dfa5aa..e2129169a 100644 --- a/src/core/changes/qcomponentremovedchange.cpp +++ b/src/core/changes/qcomponentremovedchange.cpp @@ -75,7 +75,7 @@ QComponentRemovedChangePrivate::QComponentRemovedChangePrivate(const QEntity *en */ /*! - * Constructs a new QComponentRemovedChange with \a entity and \a component. + * Constructs a new QComponentRemovedChange which will notify \a entity that \a component was removed. */ QComponentRemovedChange::QComponentRemovedChange(const QEntity *entity, const QComponent *component) @@ -84,6 +84,17 @@ QComponentRemovedChange::QComponentRemovedChange(const QEntity *entity, { } +/*! + * Constructs a new QComponentRemovedChange which will notify \a component that it was removed from \a entity + */ +QComponentRemovedChange::QComponentRemovedChange(const QComponent *component, + const QEntity *entity) + : QSceneChange(*new QComponentRemovedChangePrivate(entity, component), + ComponentRemoved, component->id()) +{ + +} + QComponentRemovedChange::~QComponentRemovedChange() { } diff --git a/src/core/changes/qcomponentremovedchange.h b/src/core/changes/qcomponentremovedchange.h index 3c57fe26f..66743b1ae 100644 --- a/src/core/changes/qcomponentremovedchange.h +++ b/src/core/changes/qcomponentremovedchange.h @@ -55,6 +55,8 @@ class QT3DCORESHARED_EXPORT QComponentRemovedChange : public QSceneChange public: explicit QComponentRemovedChange(const QEntity *entity, const QComponent *component); + explicit QComponentRemovedChange(const QComponent *component, + const QEntity *entity); ~QComponentRemovedChange(); QNodeId entityId() const Q_DECL_NOTHROW; diff --git a/src/core/changes/qpropertynodeaddedchange.cpp b/src/core/changes/qpropertynodeaddedchange.cpp index 390a170b6..9f8e50872 100644 --- a/src/core/changes/qpropertynodeaddedchange.cpp +++ b/src/core/changes/qpropertynodeaddedchange.cpp @@ -76,6 +76,16 @@ QPropertyNodeAddedChange::QPropertyNodeAddedChange(QNodeId subjectId, QNode *nod { Q_D(QPropertyNodeAddedChange); d->m_addedNodeIdTypePair = QNodeIdTypePair(node->id(), QNodePrivate::findStaticMetaObject(node->metaObject())); + + // Ensure the node has issued a node creation change. We can end + // up here if a newly created node with a parent is immediately set + // as a property on another node. In this case the deferred call to + // _q_postConstructorInit() will not have happened yet as the event + // loop will still be blocked. So force it here and we catch this + // eventuality in the _q_postConstructorInit() function so that we + // do not repeat the creation and new child scene change events. + if (node) + QNodePrivate::get(node)->_q_postConstructorInit(); } /*! \internal */ diff --git a/src/core/nodes/qcomponent.cpp b/src/core/nodes/qcomponent.cpp index 8e337adf6..f4e59e058 100644 --- a/src/core/nodes/qcomponent.cpp +++ b/src/core/nodes/qcomponent.cpp @@ -73,7 +73,7 @@ void QComponentPrivate::addEntity(QEntity *entity) m_scene->addEntityForComponent(m_id, entity->id()); } - const auto componentAddedChange = QComponentAddedChangePtr::create(entity, q); + const auto componentAddedChange = QComponentAddedChangePtr::create(q, entity); notifyObservers(componentAddedChange); Q_EMIT q->addedToEntity(entity); } @@ -86,7 +86,7 @@ void QComponentPrivate::removeEntity(QEntity *entity) m_entities.removeAll(entity); - const auto componentRemovedChange = QComponentRemovedChangePtr::create(entity, q); + const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, entity); notifyObservers(componentRemovedChange); Q_EMIT q->removedFromEntity(entity); } diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp index dbe3fd102..083fda7df 100644 --- a/src/core/nodes/qnode.cpp +++ b/src/core/nodes/qnode.cpp @@ -74,6 +74,7 @@ QNodePrivate::QNodePrivate() , m_blockNotifications(false) , m_hasBackendNode(false) , m_enabled(true) + , m_notifiedParent(false) , m_defaultPropertyTrackMode(QNode::TrackFinalValues) , m_propertyChangesSetup(false) , m_signals(this) @@ -210,13 +211,19 @@ void QNodePrivate::_q_addChild(QNode *childNode) Q_ASSERT(childNode); Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node"); + // Have we already notified the parent about its new child? If so, bail out + // early so that we do not send more than one new child event to the backend + QNodePrivate *childD = QNodePrivate::get(childNode); + if (childD->m_notifiedParent == true) + return; + // Store our id as the parentId in the child so that even if the child gets // removed from the scene as part of the destruction of the parent, when the // parent's children are deleted in the QObject dtor, we still have access to // the parentId. If we didn't store this, we wouldn't have access at that time // because the parent would then only be a QObject, the QNode part would have // been destroyed already. - QNodePrivate::get(childNode)->m_parentId = m_id; + childD->m_parentId = m_id; if (!m_scene) return; @@ -224,6 +231,11 @@ void QNodePrivate::_q_addChild(QNode *childNode) // We need to send a QPropertyNodeAddedChange to the backend // to notify the backend that we have a new child if (m_changeArbiter != nullptr) { + // Flag that we have notified the parent. We do this immediately before + // creating the change because that recurses back into this function and + // we need to catch that to avoid sending more than one new child event + // to the backend. + childD->m_notifiedParent = true; const auto change = QPropertyNodeAddedChangePtr::create(m_id, childNode); change->setPropertyName("children"); notifyObservers(change); @@ -299,6 +311,9 @@ void QNodePrivate::_q_setParentHelper(QNode *parent) notifyDestructionChangesAndRemoveFromScene(); } + // Flag that we need to notify any new parent + m_notifiedParent = false; + // Basically QObject::setParent but for QObjectPrivate QObjectPrivate::setParent_helper(parent); QNode *newParentNode = q->parentNode(); @@ -373,28 +388,43 @@ void QNodePrivate::propertyChanged(int propertyIndex) if (m_blockNotifications) return; + const auto toBackendValue = [this](const QVariant &data) -> QVariant + { + if (data.canConvert<QNode*>()) { + QNode *node = data.value<QNode*>(); + + // Ensure the node has issued a node creation change. We can end + // up here if a newly created node with a parent is immediately set + // as a property on another node. In this case the deferred call to + // _q_postConstructorInit() will not have happened yet as the event + // loop will still be blocked. So force it here and we catch this + // eventuality in the _q_postConstructorInit() function so that we + // do not repeat the creation and new child scene change events. + if (node) + QNodePrivate::get(node)->_q_postConstructorInit(); + + const QNodeId id = node ? node->id() : QNodeId(); + return QVariant::fromValue(id); + } + + return data; + }; + Q_Q(QNode); const QMetaProperty property = q->metaObject()->property(propertyIndex); const QVariant data = property.read(q); - if (data.canConvert<QNode*>()) { - QNode *node = data.value<QNode*>(); - - // Ensure the node has issued a node creation change. We can end - // up here if a newly created node with a parent is immediately set - // as a property on another node. In this case the deferred call to - // _q_postConstructorInit() will not have happened yet as the event - // loop will still be blocked. So force it here and we catch this - // eventuality in the _q_postConstructorInit() function so that we - // do not repeat the creation and new child scene change events. - if (node) - QNodePrivate::get(node)->_q_postConstructorInit(); - - const QNodeId id = node ? node->id() : QNodeId(); - notifyPropertyChange(property.name(), QVariant::fromValue(id)); + + if (data.type() == QVariant::List) { + QSequentialIterable iterable = data.value<QSequentialIterable>(); + QVariantList variants; + variants.reserve(iterable.size()); + for (const auto &v : iterable) + variants.append(toBackendValue(v)); + notifyPropertyChange(property.name(), variants); } else { - notifyPropertyChange(property.name(), data); + notifyPropertyChange(property.name(), toBackendValue(data)); } } diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h index ad9d2376e..87a0226f1 100644 --- a/src/core/nodes/qnode_p.h +++ b/src/core/nodes/qnode_p.h @@ -100,6 +100,7 @@ public: bool m_blockNotifications; bool m_hasBackendNode; bool m_enabled; + bool m_notifiedParent; QNode::PropertyTrackingMode m_defaultPropertyTrackMode; QHash<QString, QNode::PropertyTrackingMode> m_trackedPropertiesOverrides; @@ -137,10 +138,11 @@ public: static const QMetaObject *findStaticMetaObject(const QMetaObject *metaObject); + void _q_postConstructorInit(); + private: void notifyCreationChange(); void notifyDestructionChangesAndRemoveFromScene(); - void _q_postConstructorInit(); void _q_addChild(QNode *childNode); void _q_removeChild(QNode *childNode); void _q_setParentHelper(QNode *parent); diff --git a/src/doc/src/qt3danimation-module.qdoc b/src/doc/src/qt3danimation-module.qdoc index ed3406aa4..a926df67d 100644 --- a/src/doc/src/qt3danimation-module.qdoc +++ b/src/doc/src/qt3danimation-module.qdoc @@ -30,7 +30,7 @@ \module Qt3DAnimation \title Qt 3D Animation C++ Classes - \brief The Qt 3D Animation modules provides a set of prebuilt elements to help + \brief The Qt 3D Animation module provides a set of prebuilt elements to help you get started with Qt 3D. This module is still in tech preview. This means it is unstable, likely to diff --git a/src/doc/src/qt3dextras-module.qdoc b/src/doc/src/qt3dextras-module.qdoc index b9a7cc28a..facd4ef84 100644 --- a/src/doc/src/qt3dextras-module.qdoc +++ b/src/doc/src/qt3dextras-module.qdoc @@ -41,7 +41,7 @@ \module Qt3DExtras \title Qt 3D Extras C++ Classes - \brief The Qt 3D Extras modules provides a set of prebuilt elements to help + \brief The Qt 3D Extras module provides a set of prebuilt elements to help you get started with Qt 3D. This module is still in tech preview. This means it is unstable, likely to diff --git a/src/plugins/sceneparsers/assimp/assimpimporter.cpp b/src/plugins/sceneparsers/assimp/assimpimporter.cpp index 6611a2529..50fd59b38 100644 --- a/src/plugins/sceneparsers/assimp/assimpimporter.cpp +++ b/src/plugins/sceneparsers/assimp/assimpimporter.cpp @@ -227,13 +227,13 @@ QAttribute *createAttribute(QBuffer *buffer, return attribute; } -QAttribute *createAttribute(QBuffer *buffer, - QAttribute::VertexBaseType vertexBaseType, - uint vertexSize, - uint count, - uint byteOffset = 0, - uint byteStride = 0, - QNode *parent = nullptr) +QAttribute *createIndexAttribute(QBuffer *buffer, + QAttribute::VertexBaseType vertexBaseType, + uint vertexSize, + uint count, + uint byteOffset = 0, + uint byteStride = 0, + QNode *parent = nullptr) { QAttribute *attribute = QAbstractNodeFactory::createNode<QAttribute>("QAttribute"); attribute->setBuffer(buffer); @@ -603,7 +603,8 @@ void AssimpImporter::readSceneFile(const QString &path) aiProcess_GenSmoothNormals| aiProcess_FlipUVs); if (m_scene->m_aiScene == nullptr) { - qCWarning(AssimpImporterLog) << "Assimp scene import failed"; + qCWarning(AssimpImporterLog) << "Assimp scene import failed" << m_scene->m_importer->GetErrorString(); + QSceneImporter::logError(QString::fromUtf8(m_scene->m_importer->GetErrorString())); return ; } parse(); @@ -828,7 +829,7 @@ QGeometryRenderer *AssimpImporter::loadMesh(uint meshIndex) indexBuffer->setData(ibufferContent); // Add indices attributes - QAttribute *indexAttribute = createAttribute(indexBuffer, indiceType, 1, indices); + QAttribute *indexAttribute = createIndexAttribute(indexBuffer, indiceType, 1, indices); indexAttribute->setAttributeType(QAttribute::IndexAttribute); meshGeometry->addAttribute(indexAttribute); diff --git a/src/plugins/sceneparsers/assimp/assimpimporter.h b/src/plugins/sceneparsers/assimp/assimpimporter.h index 068f6eed2..e8eda3079 100644 --- a/src/plugins/sceneparsers/assimp/assimpimporter.h +++ b/src/plugins/sceneparsers/assimp/assimpimporter.h @@ -143,7 +143,6 @@ private: QHash<aiTextureType, QString> m_textureToParameterName; QVector<Qt3DAnimation::QKeyframeAnimation *> m_animations; QVector<Qt3DAnimation::QMorphingAnimation *> m_morphAnimations; -// QMap<aiNode*, Light*> m_lights; }; QDir m_sceneDir; diff --git a/src/render/backend/buffervisitor_p.h b/src/render/backend/buffervisitor_p.h index 7149e21ae..f7214460f 100644 --- a/src/render/backend/buffervisitor_p.h +++ b/src/render/backend/buffervisitor_p.h @@ -95,39 +95,7 @@ public: Q_UNUSED(ndx); Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(z); Q_UNUSED(w); } - bool apply(const GeometryRenderer *renderer, const QString &attributeName) - { - if (renderer == nullptr || renderer->instanceCount() != 1) { - return false; - } - - Geometry *geom = m_manager->lookupResource<Geometry, GeometryManager>(renderer->geometryId()); - - if (!geom) - return false; - - Attribute *attribute = nullptr; - - const auto attrIds = geom->attributes(); - for (const Qt3DCore::QNodeId attrId : attrIds) { - attribute = m_manager->lookupResource<Attribute, AttributeManager>(attrId); - if (attribute){ - if (attribute->name() == attributeName - || (attributeName == QStringLiteral("default") - && attribute->name() == QAttribute::defaultTextureCoordinateAttributeName())) { - break; - } - } - attribute = nullptr; - } - - if (!attribute) - return false; - - return apply(attribute); - } - - bool apply(Qt3DRender::Render::Attribute *attribute) + bool apply(Qt3DRender::Render::Attribute *attribute, Qt3DRender::Render::Attribute *indexAttribute, int drawVertexCount) { if (attribute->vertexBaseType() != VertexBaseType) return false; @@ -136,12 +104,36 @@ public: auto data = m_manager->lookupResource<Buffer, BufferManager>(attribute->bufferId())->data(); auto buffer = BufferTypeInfo::castToType<VertexBaseType>(data, attribute->byteOffset()); - switch (dataSize) { - case 1: traverseCoordinates1(buffer, attribute->byteStride(), attribute->count()); break; - case 2: traverseCoordinates2(buffer, attribute->byteStride(), attribute->count()); break; - case 3: traverseCoordinates3(buffer, attribute->byteStride(), attribute->count()); break; - case 4: traverseCoordinates4(buffer, attribute->byteStride(), attribute->count()); break; - default: Q_UNREACHABLE(); + + if (indexAttribute) { + auto indexData = m_manager->lookupResource<Buffer, BufferManager>(indexAttribute->bufferId())->data(); + if (indexAttribute->vertexBaseType() == QAttribute::UnsignedShort) { + auto indexBuffer = BufferTypeInfo::castToType<QAttribute::UnsignedShort>(indexData, indexAttribute->byteOffset()); + switch (dataSize) { + case 1: traverseCoordinates1Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 2: traverseCoordinates2Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 3: traverseCoordinates3Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 4: traverseCoordinates4Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + default: Q_UNREACHABLE(); + } + } else { + auto indexBuffer = BufferTypeInfo::castToType<QAttribute::UnsignedInt>(indexData, indexAttribute->byteOffset()); + switch (dataSize) { + case 1: traverseCoordinates1Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 2: traverseCoordinates2Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 3: traverseCoordinates3Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + case 4: traverseCoordinates4Indexed(buffer, attribute->byteStride(), indexBuffer, drawVertexCount); break; + default: Q_UNREACHABLE(); + } + } + } else { + switch (dataSize) { + case 1: traverseCoordinates1(buffer, attribute->byteStride(), drawVertexCount); break; + case 2: traverseCoordinates2(buffer, attribute->byteStride(), drawVertexCount); break; + case 3: traverseCoordinates3(buffer, attribute->byteStride(), drawVertexCount); break; + case 4: traverseCoordinates4(buffer, attribute->byteStride(), drawVertexCount); break; + default: Q_UNREACHABLE(); + } } return true; @@ -161,42 +153,95 @@ protected: } } + template <typename Coordinate, typename IndexElem> + void traverseCoordinates1Indexed(Coordinate *coordinates, + const uint byteStride, + IndexElem *indices, + const uint count) + { + const uint stride = byteStride / sizeof(Coordinate); + for (uint i = 0; i < count; ++i) { + const uint n = stride * indices[i]; + visit(i, coordinates[n]); + } + } + template <typename Coordinate> void traverseCoordinates2(Coordinate *coordinates, const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1]); coordinates += stride; } } + + template <typename Coordinate, typename IndexElem> + void traverseCoordinates2Indexed(Coordinate *coordinates, + const uint byteStride, + IndexElem *indices, + const uint count) + { + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2; + for (uint i = 0; i < count; ++i) { + const uint n = stride * indices[i]; + visit(i, coordinates[n], coordinates[n + 1]); + } + } + template <typename Coordinate> void traverseCoordinates3(Coordinate *coordinates, const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1], coordinates[2]); coordinates += stride; } } + template <typename Coordinate, typename IndexElem> + void traverseCoordinates3Indexed(Coordinate *coordinates, + const uint byteStride, + IndexElem *indices, + const uint count) + { + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3; + for (uint i = 0; i < count; ++i) { + const uint n = stride * indices[i]; + visit(i, coordinates[n], coordinates[n + 1], coordinates[n + 2]); + } + } + template <typename Coordinate> void traverseCoordinates4(Coordinate *coordinates, const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1], coordinates[2], coordinates[3]); coordinates += stride; } } + template <typename Coordinate, typename IndexElem> + void traverseCoordinates4Indexed(Coordinate *coordinates, + const uint byteStride, + IndexElem *indices, + const uint count) + { + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4; + for (uint i = 0; i < count; ++i) { + const uint n = stride * indices[i]; + visit(i, coordinates[n], coordinates[n + 1], coordinates[n + 2], coordinates[n + 3]); + } + } + NodeManagers *m_manager; }; diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index f4440a7f8..092340cf1 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -2000,7 +2000,7 @@ void Renderer::cleanGraphicsResources() } } -QList<QMouseEvent> Renderer::pendingPickingEvents() const +QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const { return m_pickEventFilter->pendingMouseEvents(); } diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h index 2654b3b61..b5c8d2c4c 100644 --- a/src/render/backend/renderer_p.h +++ b/src/render/backend/renderer_p.h @@ -260,7 +260,7 @@ public: inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } - QList<QMouseEvent> pendingPickingEvents() const; + QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const; QList<QKeyEvent> pendingKeyEvents() const; void addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId); diff --git a/src/render/backend/renderview.cpp b/src/render/backend/renderview.cpp index da3d551e6..9e8177c0d 100644 --- a/src/render/backend/renderview.cpp +++ b/src/render/backend/renderview.cpp @@ -753,14 +753,19 @@ void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, c // ShaderData/Buffers would be handled as UBO/SSBO and would therefore // not be in the default uniform block if (value.valueType() == UniformValue::NodeId) { - const Qt3DCore::QNodeId texId = *value.constData<Qt3DCore::QNodeId>(); - const Texture *tex = m_manager->textureManager()->lookupResource(texId); - if (tex != nullptr) { - uniformPack.setTexture(nameId, texId); - UniformValue::Texture textureValue; - textureValue.nodeId = texId; - uniformPack.setUniform(nameId, UniformValue(textureValue)); + const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); + + const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); + for (int i = 0; i < uniformArraySize; ++i) { + const Qt3DCore::QNodeId texId = nodeIds[i]; + const Texture *tex = m_manager->textureManager()->lookupResource(texId); + if (tex != nullptr) + uniformPack.setTexture(nameId, i, texId); } + + UniformValue textureValue(uniformArraySize * sizeof(int), UniformValue::TextureValue); + std::fill(textureValue.data<int>(), textureValue.data<int>() + uniformArraySize, -1); + uniformPack.setUniform(nameId, textureValue); } else { uniformPack.setUniform(nameId, value); } diff --git a/src/render/backend/shaderparameterpack.cpp b/src/render/backend/shaderparameterpack.cpp index 01a977aee..f78e45a5e 100644 --- a/src/render/backend/shaderparameterpack.cpp +++ b/src/render/backend/shaderparameterpack.cpp @@ -65,17 +65,17 @@ void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &v m_uniforms.insert(glslNameId, val); } -void ShaderParameterPack::setTexture(const int glslNameId, Qt3DCore::QNodeId texId) +void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId texId) { for (int t=0; t<m_textures.size(); ++t) { - if (m_textures[t].glslNameId != glslNameId) + if (m_textures[t].glslNameId != glslNameId || m_textures[t].uniformArrayIndex != uniformArrayIndex) continue; m_textures[t].texId = texId; return; } - m_textures.append(NamedTexture(glslNameId, texId)); + m_textures.append(NamedTexture(glslNameId, texId, uniformArrayIndex)); } // Contains Uniform Block Index and QNodeId of the ShaderData (UBO) diff --git a/src/render/backend/shaderparameterpack_p.h b/src/render/backend/shaderparameterpack_p.h index c0ab05e57..abd63a187 100644 --- a/src/render/backend/shaderparameterpack_p.h +++ b/src/render/backend/shaderparameterpack_p.h @@ -96,7 +96,7 @@ public: ~ShaderParameterPack(); void setUniform(const int glslNameId, const UniformValue &val); - void setTexture(const int glslNameId, Qt3DCore::QNodeId id); + void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id); void setUniformBuffer(BlockToUBO blockToUBO); void setShaderStorageBuffer(BlockToSSBO blockToSSBO); void setSubmissionUniform(const ShaderUniform &uniform); @@ -108,13 +108,15 @@ public: struct NamedTexture { NamedTexture() {} - NamedTexture(const int nm, Qt3DCore::QNodeId t) - : glslNameId(nm) - , texId(t) + NamedTexture(const int glslNameId, Qt3DCore::QNodeId texId, int uniformArrayIndex) + : glslNameId(glslNameId) + , texId(texId) + , uniformArrayIndex(uniformArrayIndex) { } int glslNameId; Qt3DCore::QNodeId texId; + int uniformArrayIndex; }; inline QVector<NamedTexture> textures() const { return m_textures; } diff --git a/src/render/backend/trianglesvisitor.cpp b/src/render/backend/trianglesvisitor.cpp index 4a05d8f19..87ba7bde9 100644 --- a/src/render/backend/trianglesvisitor.cpp +++ b/src/render/backend/trianglesvisitor.cpp @@ -85,8 +85,8 @@ void traverseTrianglesIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -111,8 +111,8 @@ void traverseTriangles(vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -147,8 +147,8 @@ void traverseTriangleStripIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -178,8 +178,8 @@ void traverseTriangleStrip(vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -204,8 +204,8 @@ void traverseTriangleFanIndexed(index *indices, const BufferInfo &vertexInfo, TrianglesVisitor* visitor) { - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -234,8 +234,8 @@ void traverseTriangleFan(vertex *vertices, const BufferInfo &vertexInfo, TrianglesVisitor* visitor) { - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -268,8 +268,8 @@ void traverseTriangleAdjacencyIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -294,8 +294,8 @@ void traverseTriangleAdjacency(Vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(Vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(Vertex) : maxVerticesDataSize; uint ndx[3]; Vector3D abc[3]; @@ -315,7 +315,7 @@ void traverseTriangleAdjacency(Vertex *vertices, template<typename Coordinate> Vector4D readCoordinate(const BufferInfo &info, Coordinate *coordinates, uint index) { - const uint stride = info.byteStride / sizeof(Coordinate); + const uint stride = info.byteStride ? info.byteStride / sizeof(Coordinate) : info.dataSize; Vector4D ret(0, 0, 0, 1.0f); coordinates += stride * index; for (uint e = 0; e < info.dataSize; ++e) diff --git a/src/render/backend/uniform.cpp b/src/render/backend/uniform.cpp index aedba5375..41ee24967 100644 --- a/src/render/backend/uniform.cpp +++ b/src/render/backend/uniform.cpp @@ -56,10 +56,12 @@ int byteSizeForMetaType(int type) { if (type == qMatrix4x4TypeId) return sizeof(Matrix4x4); - if (type == qVector3DTypeId) + if (type == qVector3DTypeId) return sizeof(Vector3D); - if (type == qVector4DTypeId) + if (type == qVector4DTypeId) return sizeof(Vector4D); + if (type == qNodeIdTypeId) + return sizeof(Qt3DCore::QNodeId); switch (type) { case QMetaType::Bool: @@ -230,6 +232,11 @@ UniformValue UniformValue::fromVariant(const QVariant &variant) break; const int listEntryType = variants.first().userType(); + + // array of textures + if (listEntryType == qNodeIdTypeId) + v.m_valueType = NodeId; + const int stride = byteSizeForMetaType(listEntryType) / sizeof(float); // Resize v.m_data v.m_data.resize(stride * variants.size()); diff --git a/src/render/backend/uniform_p.h b/src/render/backend/uniform_p.h index 213cf2606..e31aaa609 100644 --- a/src/render/backend/uniform_p.h +++ b/src/render/backend/uniform_p.h @@ -114,11 +114,6 @@ public: BufferValue }; - struct Texture { - int textureId = 0; // Set first so that glUniform1iv will work - Qt3DCore::QNodeId nodeId; - }; - // UniformValue implicitely converts doubles to floats to ensure // correct rendering behavior for the cases where Qt3D parameters created from // a double or QVariant(double) are used to fill uniform values that in reality @@ -177,6 +172,13 @@ public: } } + // Reserve data to be filled in later + UniformValue(int byteSize, ValueType valueType) + : m_data(byteSize / sizeof(float)) + , m_valueType(valueType) + { + } + // For nodes which will later be replaced by a Texture or Buffer UniformValue(Qt3DCore::QNodeId id) : UniformValue() @@ -185,14 +187,6 @@ public: memcpy(m_data.data(), &id, sizeof(Qt3DCore::QNodeId)); } - // For textures - UniformValue(UniformValue::Texture t) - : UniformValue() - { - m_valueType = TextureValue; - memcpy(m_data.data(), &t, sizeof(Texture)); - } - ValueType valueType() const { return m_valueType; } UniformType storedType() const { return m_storedType; } diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp index 298f6bf91..5c3128f30 100644 --- a/src/render/graphicshelpers/graphicscontext.cpp +++ b/src/render/graphicshelpers/graphicscontext.cpp @@ -499,14 +499,14 @@ void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderPro void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) { - QOpenGLShaderProgram *shaderProgram = m_shaderCache.getShaderProgramAndAddRef(shader->dna(), shader->peerId()); - if (!shaderProgram) { + bool wasPresent = false; + QOpenGLShaderProgram *shaderProgram = m_shaderCache.getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent); + if (!shaderProgram && !wasPresent) { // No matching QOpenGLShader in the cache so create one shaderProgram = createShaderProgram(shader); - // Store in cache - if (shaderProgram) - m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram); + // Store in cache (even when failed and shaderProgram is null) + m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram); } // Ensure the Shader node knows about the program interface @@ -1213,7 +1213,7 @@ bool GraphicsContext::setParameters(ShaderParameterPack ¶meterPack) UniformValue &texUniform = uniformValues[namedTex.glslNameId]; Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue); const int texUnit = activateTexture(TextureScopeMaterial, t); - texUniform.data<UniformValue::Texture>()->textureId = texUnit; + texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; // if the texture data from generators may not be available yet, // make sure that the next frame is rendered if (texUnit == -1) @@ -1263,11 +1263,10 @@ bool GraphicsContext::setParameters(ShaderParameterPack ¶meterPack) for (const ShaderUniform &uniform : activeUniforms) { // We can use [] as we are sure the the uniform wouldn't // be un activeUniforms if there wasn't a matching value - const auto &v = values[uniform.m_nameId]; + const UniformValue &v = values[uniform.m_nameId]; // skip invalid textures - if (v.valueType() == UniformValue::TextureValue && - v.constData<UniformValue::Texture>()->textureId == -1) + if (v.valueType() == UniformValue::TextureValue && *v.constData<int>() == -1) continue; applyUniform(uniform, v); diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp index 7504b4ebd..aaee51e16 100644 --- a/src/render/jobs/calcboundingvolumejob.cpp +++ b/src/render/jobs/calcboundingvolumejob.cpp @@ -81,10 +81,10 @@ public: const Sphere& result() { return m_volume; } - bool apply(Qt3DRender::Render::Attribute *positionAttribute) + bool apply(Qt3DRender::Render::Attribute *positionAttribute, Qt3DRender::Render::Attribute *indexAttribute, int drawVertexCount) { FindExtremePoints findExtremePoints(m_manager); - if (!findExtremePoints.apply(positionAttribute)) + if (!findExtremePoints.apply(positionAttribute, indexAttribute, drawVertexCount)) return false; // Calculate squared distance for the pairs of points @@ -109,7 +109,7 @@ public: m_volume.setRadius((q - c).length()); ExpandSphere expandSphere(m_manager, m_volume); - if (!expandSphere.apply(positionAttribute)) + if (!expandSphere.apply(positionAttribute, indexAttribute, drawVertexCount)) return false; return true; @@ -191,10 +191,12 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node) return; GeometryRenderer *gRenderer = node->renderComponent<GeometryRenderer>(); - if (gRenderer) { + if (gRenderer && gRenderer->primitiveType() != QGeometryRenderer::Patches) { Geometry *geom = manager->lookupResource<Geometry, GeometryManager>(gRenderer->geometryId()); if (geom) { + int drawVertexCount = gRenderer->vertexCount(); // may be 0, gets changed below if so + Qt3DRender::Render::Attribute *positionAttribute = manager->lookupResource<Attribute, AttributeManager>(geom->boundingPositionAttribute()); // Use the default position attribute if attribute is null @@ -212,29 +214,60 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node) || positionAttribute->attributeType() != QAttribute::VertexAttribute || positionAttribute->vertexBaseType() != QAttribute::Float || positionAttribute->vertexSize() < 3) { - qWarning() << "QGeometry::boundingVolumePositionAttribute position Attribute not suited for bounding volume computation"; + qWarning("calculateLocalBoundingVolume: Position attribute not suited for bounding volume computation"); 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"; + qWarning("calculateLocalBoundingVolume: Position attribute not referencing a valid buffer"); return; } + // Check if there is an index attribute. + Qt3DRender::Render::Attribute *indexAttribute = nullptr; + Buffer *indexBuf = nullptr; + const QVector<Qt3DCore::QNodeId> attributes = geom->attributes(); + + for (Qt3DCore::QNodeId attrNodeId : attributes) { + Qt3DRender::Render::Attribute *attr = manager->lookupResource<Attribute, AttributeManager>(attrNodeId); + if (attr && attr->attributeType() == Qt3DRender::QAttribute::IndexAttribute) { + indexBuf = manager->lookupResource<Buffer, BufferManager>(attr->bufferId()); + if (indexBuf) { + indexAttribute = attr; + + if (!drawVertexCount) + drawVertexCount = indexAttribute->count(); + + if (indexAttribute->vertexBaseType() != QAttribute::UnsignedShort + && indexAttribute->vertexBaseType() != QAttribute::UnsignedInt) + { + qWarning("calculateLocalBoundingVolume: Unsupported index attribute type"); + return; + } + + break; + } + } + } + + if (!indexAttribute && !drawVertexCount) + drawVertexCount = positionAttribute->count(); + // 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()) { - + if (buf->isDirty() + || node->isBoundingVolumeDirty() + || positionAttribute->isDirty() + || geom->isDirty() + || gRenderer->isDirty() + || (indexAttribute && indexAttribute->isDirty()) + || (indexBuf && indexBuf->isDirty())) { BoundingVolumeCalculator reader(manager); - if (reader.apply(positionAttribute)) { + if (reader.apply(positionAttribute, indexAttribute, drawVertexCount)) { node->localBoundingVolume()->setCenter(reader.result().center()); node->localBoundingVolume()->setRadius(reader.result().radius()); node->unsetBoundingVolumeDirty(); diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp index 902c048e6..23a553084 100644 --- a/src/render/jobs/pickboundingvolumejob.cpp +++ b/src/render/jobs/pickboundingvolumejob.cpp @@ -54,6 +54,10 @@ #include <Qt3DRender/private/qpickevent_p.h> #include <Qt3DRender/private/pickboundingvolumeutils_p.h> +#include <QSurface> +#include <QWindow> +#include <QOffscreenSurface> + QT_BEGIN_NAMESPACE namespace Qt3DRender { @@ -119,7 +123,7 @@ void PickBoundingVolumeJob::setRoot(Entity *root) m_node = root; } -void PickBoundingVolumeJob::setMouseEvents(const QList<QMouseEvent> &pendingEvents) +void PickBoundingVolumeJob::setMouseEvents(const QList<QPair<QObject*, QMouseEvent>> &pendingEvents) { m_pendingMouseEvents = pendingEvents; } @@ -185,8 +189,8 @@ bool PickBoundingVolumeJob::runHelper() bool hasMoveEvent = false; bool hasOtherEvent = false; // Quickly look which types of events we've got - for (const QMouseEvent &event : mouseEvents) { - const bool isMove = (event.type() == QEvent::MouseMove); + for (const auto &event : mouseEvents) { + const bool isMove = (event.second.type() == QEvent::MouseMove); hasMoveEvent |= isMove; hasOtherEvent |= !isMove; } @@ -214,10 +218,10 @@ bool PickBoundingVolumeJob::runHelper() PickingUtils::ViewportCameraAreaGatherer vcaGatherer; // TO DO: We could cache this and only gather when we know the FrameGraph tree has changed - const QVector<PickingUtils::ViewportCameraAreaTriplet> vcaTriplets = vcaGatherer.gather(m_frameGraphRoot); + const QVector<PickingUtils::ViewportCameraAreaDetails> vcaDetails = vcaGatherer.gather(m_frameGraphRoot); // If we have no viewport / camera or area, return early - if (vcaTriplets.empty()) + if (vcaDetails.empty()) return false; // TO DO: @@ -236,19 +240,21 @@ bool PickBoundingVolumeJob::runHelper() const float pickWorldSpaceTolerance = m_renderSettings->pickWorldSpaceTolerance(); // For each mouse event - for (const QMouseEvent &event : mouseEvents) { + for (const auto &event : mouseEvents) { m_hoveredPickersToClear = m_hoveredPickers; QPickEvent::Buttons eventButton = QPickEvent::NoButton; int eventButtons = 0; int eventModifiers = QPickEvent::NoModifier; - setEventButtonAndModifiers(event, eventButton, eventButtons, eventModifiers); + setEventButtonAndModifiers(event.second, eventButton, eventButtons, eventModifiers); - // For each triplet of Viewport / Camera and Area - for (const PickingUtils::ViewportCameraAreaTriplet &vca : vcaTriplets) { + // For each Viewport / Camera and Area entry + for (const PickingUtils::ViewportCameraAreaDetails &vca : vcaDetails) { PickingUtils::HitList sphereHits; - QRay3D ray = rayForViewportAndCamera(vca.area, event.pos(), vca.viewport, vca.cameraId); + QRay3D ray = rayForViewportAndCamera(vca, event.first, event.second); + if (!ray.isValid()) + continue; PickingUtils::HierarchicalEntityPicker entityPicker(ray); if (entityPicker.collectHits(m_node)) { @@ -285,7 +291,7 @@ bool PickBoundingVolumeJob::runHelper() } // Dispatch events based on hit results - dispatchPickEvents(event, sphereHits, eventButton, eventButtons, eventModifiers, allHitsRequested); + dispatchPickEvents(event.second, sphereHits, eventButton, eventButtons, eventModifiers, allHitsRequested); } } @@ -479,7 +485,7 @@ void PickBoundingVolumeJob::viewMatrixForCamera(Qt3DCore::QNodeId cameraId, if (camNode != nullptr && (lens = camNode->renderComponent<CameraLens>()) != nullptr && lens->isEnabled()) { - viewMatrix = *camNode->worldTransform(); + viewMatrix = lens->viewMatrix(*camNode->worldTransform()); projectionMatrix = lens->projection(); } } @@ -497,18 +503,38 @@ QRect PickBoundingVolumeJob::windowViewport(const QSize &area, const QRectF &rel return relativeViewport.toRect(); } -RayCasting::QRay3D PickBoundingVolumeJob::rayForViewportAndCamera(const QSize &area, - const QPoint &pos, - const QRectF &relativeViewport, - const Qt3DCore::QNodeId cameraId) const +RayCasting::QRay3D PickBoundingVolumeJob::rayForViewportAndCamera(const PickingUtils::ViewportCameraAreaDetails &vca, + QObject *eventSource, + const QMouseEvent &event) const { + static RayCasting::QRay3D invalidRay({}, {}, 0.f); + Matrix4x4 viewMatrix; Matrix4x4 projectionMatrix; - viewMatrixForCamera(cameraId, viewMatrix, projectionMatrix); - const QRect viewport = windowViewport(area, relativeViewport); + viewMatrixForCamera(vca.cameraId, viewMatrix, projectionMatrix); + const QRect viewport = windowViewport(vca.area, vca.viewport); + const QPoint pos = event.pos(); + + if (vca.area.isValid() && !viewport.contains(pos)) + return invalidRay; + if (vca.surface) { + QSurface *surface = nullptr; + if (eventSource) { + QWindow *window = qobject_cast<QWindow *>(eventSource); + if (window) { + surface = static_cast<QSurface *>(window); + } else { + QOffscreenSurface *offscreen = qobject_cast<QOffscreenSurface *>(eventSource); + if (offscreen) + surface = static_cast<QSurface *>(offscreen); + } + } + if (surface && vca.surface != surface) + return invalidRay; + } // In GL the y is inverted compared to Qt - const QPoint glCorrectPos = QPoint(pos.x(), area.isValid() ? area.height() - pos.y() : pos.y()); + const QPoint glCorrectPos = QPoint(pos.x(), vca.area.isValid() ? vca.area.height() - pos.y() : pos.y()); const auto ray = intersectionRay(glCorrectPos, viewMatrix, projectionMatrix, viewport); return ray; } diff --git a/src/render/jobs/pickboundingvolumejob_p.h b/src/render/jobs/pickboundingvolumejob_p.h index 5d4f63f77..016d1ba0b 100644 --- a/src/render/jobs/pickboundingvolumejob_p.h +++ b/src/render/jobs/pickboundingvolumejob_p.h @@ -56,6 +56,7 @@ #include <Qt3DRender/private/handle_types_p.h> #include <Qt3DRender/private/qboundingvolumeprovider_p.h> #include <Qt3DRender/private/qcollisionqueryresult_p.h> +#include <Qt3DRender/private/pickboundingvolumeutils_p.h> #include <Qt3DRender/qpickevent.h> #include <QMouseEvent> #include <QKeyEvent> @@ -85,7 +86,7 @@ public: PickBoundingVolumeJob(); void setRoot(Entity *root); - void setMouseEvents(const QList<QMouseEvent> &pendingEvents); + void setMouseEvents(const QList<QPair<QObject*, QMouseEvent>> &pendingEvents); void setKeyEvents(const QList<QKeyEvent> &pendingEvents); void setFrameGraphRoot(FrameGraphNode *frameGraphRoot); void setRenderSettings(RenderSettings *settings); @@ -117,7 +118,7 @@ private: Entity *m_node; FrameGraphNode *m_frameGraphRoot; RenderSettings *m_renderSettings; - QList<QMouseEvent> m_pendingMouseEvents; + QList<QPair<QObject*, QMouseEvent>> m_pendingMouseEvents; bool m_pickersDirty; bool m_oneEnabledAtLeast; bool m_oneHoverAtLeast; @@ -128,10 +129,9 @@ private: Matrix4x4 &viewMatrix, Matrix4x4 &projectionMatrix) const; QRect windowViewport(const QSize &area, const QRectF &relativeViewport) const; - RayCasting::QRay3D rayForViewportAndCamera(const QSize &area, - const QPoint &pos, - const QRectF &relativeViewport, - const Qt3DCore::QNodeId cameraId) const; + RayCasting::QRay3D rayForViewportAndCamera(const PickingUtils::ViewportCameraAreaDetails &vca, + QObject *eventSource, + const QMouseEvent &event) const; void clearPreviouslyHoveredPickers(); HObjectPicker m_currentPicker; QVector<HObjectPicker> m_hoveredPickers; diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp index ae80b0a23..fb45d563f 100644 --- a/src/render/jobs/pickboundingvolumeutils.cpp +++ b/src/render/jobs/pickboundingvolumeutils.cpp @@ -72,9 +72,9 @@ void ViewportCameraAreaGatherer::visit(FrameGraphNode *node) m_leaves.push_back(node); } -ViewportCameraAreaTriplet ViewportCameraAreaGatherer::gatherUpViewportCameraAreas(Render::FrameGraphNode *node) const +ViewportCameraAreaDetails ViewportCameraAreaGatherer::gatherUpViewportCameraAreas(Render::FrameGraphNode *node) const { - ViewportCameraAreaTriplet vca; + ViewportCameraAreaDetails vca; vca.viewport = QRectF(0.0f, 0.0f, 1.0f, 1.0f); while (node) { @@ -86,9 +86,12 @@ ViewportCameraAreaTriplet ViewportCameraAreaGatherer::gatherUpViewportCameraArea case FrameGraphNode::Viewport: vca.viewport = computeViewport(vca.viewport, static_cast<const ViewportNode *>(node)); break; - case FrameGraphNode::Surface: - vca.area = static_cast<const RenderSurfaceSelector *>(node)->renderTargetSize(); + case FrameGraphNode::Surface: { + auto selector = static_cast<const RenderSurfaceSelector *>(node); + vca.area = selector->renderTargetSize(); + vca.surface = selector->surface(); break; + } default: break; } @@ -98,29 +101,32 @@ ViewportCameraAreaTriplet ViewportCameraAreaGatherer::gatherUpViewportCameraArea return vca; } -QVector<ViewportCameraAreaTriplet> ViewportCameraAreaGatherer::gather(FrameGraphNode *root) +QVector<ViewportCameraAreaDetails> ViewportCameraAreaGatherer::gather(FrameGraphNode *root) { // Retrieve all leaves visit(root); - QVector<ViewportCameraAreaTriplet> vcaTriplets; + QVector<ViewportCameraAreaDetails> vcaTriplets; vcaTriplets.reserve(m_leaves.count()); // Find all viewport/camera pairs by traversing from leaf to root for (Render::FrameGraphNode *leaf : qAsConst(m_leaves)) { - ViewportCameraAreaTriplet vcaTriplet = gatherUpViewportCameraAreas(leaf); - if (!m_targetCamera.isNull() && vcaTriplet.cameraId != m_targetCamera) + ViewportCameraAreaDetails vcaDetails = gatherUpViewportCameraAreas(leaf); + if (!m_targetCamera.isNull() && vcaDetails.cameraId != m_targetCamera) continue; - if (!vcaTriplet.cameraId.isNull() && isUnique(vcaTriplets, vcaTriplet)) - vcaTriplets.push_back(vcaTriplet); + if (!vcaDetails.cameraId.isNull() && isUnique(vcaTriplets, vcaDetails)) + vcaTriplets.push_back(vcaDetails); } return vcaTriplets; } -bool ViewportCameraAreaGatherer::isUnique(const QVector<ViewportCameraAreaTriplet> &vcaTriplets, - const ViewportCameraAreaTriplet &vca) const +bool ViewportCameraAreaGatherer::isUnique(const QVector<ViewportCameraAreaDetails> &vcaList, + const ViewportCameraAreaDetails &vca) const { - for (const ViewportCameraAreaTriplet &triplet : vcaTriplets) { - if (vca.cameraId == triplet.cameraId && vca.viewport == triplet.viewport && vca.area == triplet.area) + for (const ViewportCameraAreaDetails &listItem : vcaList) { + if (vca.cameraId == listItem.cameraId && + vca.viewport == listItem.viewport && + vca.surface == listItem.surface && + vca.area == listItem.area) return false; } return true; diff --git a/src/render/jobs/pickboundingvolumeutils_p.h b/src/render/jobs/pickboundingvolumeutils_p.h index 6e2532ea5..9cd6a5bbc 100644 --- a/src/render/jobs/pickboundingvolumeutils_p.h +++ b/src/render/jobs/pickboundingvolumeutils_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +class QSurface; + namespace Qt3DRender { namespace RayCasting { class QAbstractCollisionQueryService; @@ -72,27 +74,28 @@ class NodeManagers; namespace PickingUtils { -struct Q_AUTOTEST_EXPORT ViewportCameraAreaTriplet +struct Q_AUTOTEST_EXPORT ViewportCameraAreaDetails { Qt3DCore::QNodeId cameraId; QRectF viewport; QSize area; + QSurface *surface = nullptr; }; -QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, PickingUtils, ViewportCameraAreaTriplet, Q_PRIMITIVE_TYPE) +QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, PickingUtils, ViewportCameraAreaDetails, Q_PRIMITIVE_TYPE) class Q_AUTOTEST_EXPORT ViewportCameraAreaGatherer { public: ViewportCameraAreaGatherer(const Qt3DCore::QNodeId &nodeId = Qt3DCore::QNodeId()) : m_targetCamera(nodeId) { } - QVector<ViewportCameraAreaTriplet> gather(FrameGraphNode *root); + QVector<ViewportCameraAreaDetails> gather(FrameGraphNode *root); private: Qt3DCore::QNodeId m_targetCamera; QVector<FrameGraphNode *> m_leaves; void visit(FrameGraphNode *node); - ViewportCameraAreaTriplet gatherUpViewportCameraAreas(Render::FrameGraphNode *node) const; - bool isUnique(const QVector<ViewportCameraAreaTriplet> &vcaTriplets, const ViewportCameraAreaTriplet &vca) const; + ViewportCameraAreaDetails gatherUpViewportCameraAreas(Render::FrameGraphNode *node) const; + bool isUnique(const QVector<ViewportCameraAreaDetails> &vcaList, const ViewportCameraAreaDetails &vca) const; }; class Q_AUTOTEST_EXPORT EntityGatherer diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp index ad9bee8e1..141d65c22 100644 --- a/src/render/jobs/updatelevelofdetailjob.cpp +++ b/src/render/jobs/updatelevelofdetailjob.cpp @@ -175,11 +175,11 @@ void UpdateLevelOfDetailJob::updateEntityLodByScreenArea(Entity *entity, LevelOf return; PickingUtils::ViewportCameraAreaGatherer vcaGatherer(lod->camera()); - const QVector<PickingUtils::ViewportCameraAreaTriplet> vcaTriplets = vcaGatherer.gather(m_frameGraphRoot); + const QVector<PickingUtils::ViewportCameraAreaDetails> vcaTriplets = vcaGatherer.gather(m_frameGraphRoot); if (vcaTriplets.isEmpty()) return; - const PickingUtils::ViewportCameraAreaTriplet &vca = vcaTriplets.front(); + const PickingUtils::ViewportCameraAreaDetails &vca = vcaTriplets.front(); const QVector<qreal> thresholds = lod->thresholds(); Sphere bv(Vector3D(lod->center()), lod->radius()); diff --git a/src/render/materialsystem/qparameter.cpp b/src/render/materialsystem/qparameter.cpp index b9bfa44e7..2b2dd29d5 100644 --- a/src/render/materialsystem/qparameter.cpp +++ b/src/render/materialsystem/qparameter.cpp @@ -179,13 +179,30 @@ QParameterPrivate::QParameterPrivate() { } +namespace { + +/*! \internal */ +inline QVariant toBackendValue(const QVariant &v) +{ + if (auto nodeValue = v.value<Qt3DCore::QNode*>()) + return QVariant::fromValue(nodeValue->id()); + return v; +} + +} // anonymous + void QParameterPrivate::setValue(const QVariant &v) { - Qt3DCore::QNode *nodeValue = v.value<Qt3DCore::QNode *>(); - if (nodeValue != nullptr) - m_backendValue = QVariant::fromValue(nodeValue->id()); - else - m_backendValue = v; + if (v.type() == QVariant::List) { + QSequentialIterable iterable = v.value<QSequentialIterable>(); + QVariantList variants; + variants.reserve(iterable.size()); + for (const auto &v : iterable) + variants.append(toBackendValue(v)); + m_backendValue = variants; + } else { + m_backendValue = toBackendValue(v); + } m_value = v; } diff --git a/src/render/materialsystem/shadercache.cpp b/src/render/materialsystem/shadercache.cpp index 4ddf26799..ce29622ad 100644 --- a/src/render/materialsystem/shadercache.cpp +++ b/src/render/materialsystem/shadercache.cpp @@ -62,18 +62,28 @@ ShaderCache::~ShaderCache() * * \return A pointer to the shader program if it is cached, nullptr otherwise */ -QOpenGLShaderProgram *ShaderCache::getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId) +QOpenGLShaderProgram *ShaderCache::getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, bool *wasPresent) { - auto shaderProgram = m_programHash.value(dna, nullptr); - if (shaderProgram) { + auto shaderProgram = m_programHash.constFind(dna); + + // Some callers may wish to differentiate between a result of null due to + // not having anything in the cache and a result of null due to the cache + // containing a program the shaders of which failed to compile. + if (wasPresent) + *wasPresent = shaderProgram != m_programHash.constEnd(); + + if (shaderProgram != m_programHash.constEnd()) { // Ensure we store the fact that shaderPeerId references this shader QMutexLocker lock(&m_refsMutex); QVector<Qt3DCore::QNodeId> &programRefs = m_programRefs[dna]; auto it = std::lower_bound(programRefs.begin(), programRefs.end(), shaderPeerId); if (*it != shaderPeerId) programRefs.insert(it, shaderPeerId); + + return *shaderProgram; } - return shaderProgram; + + return nullptr; } /*! diff --git a/src/render/materialsystem/shadercache_p.h b/src/render/materialsystem/shadercache_p.h index 24a55876e..bda629ee5 100644 --- a/src/render/materialsystem/shadercache_p.h +++ b/src/render/materialsystem/shadercache_p.h @@ -71,7 +71,7 @@ class QT3DRENDERSHARED_PRIVATE_EXPORT ShaderCache public: ~ShaderCache(); - QOpenGLShaderProgram *getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId); + QOpenGLShaderProgram *getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, bool *wasPresent = nullptr); void insert(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, QOpenGLShaderProgram *program); void removeRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId); void purge(); diff --git a/src/render/picking/pickeventfilter.cpp b/src/render/picking/pickeventfilter.cpp index 297911e45..b10383c72 100644 --- a/src/render/picking/pickeventfilter.cpp +++ b/src/render/picking/pickeventfilter.cpp @@ -62,10 +62,10 @@ PickEventFilter::~PickEventFilter() Called from a worker thread in the thread pool so be sure to mutex protect the data. */ -QList<QMouseEvent> PickEventFilter::pendingMouseEvents() +QList<QPair<QObject *, QMouseEvent> > PickEventFilter::pendingMouseEvents() { QMutexLocker locker(&m_mutex); - QList<QMouseEvent> pendingEvents(m_pendingMouseEvents); + QList<QPair<QObject*, QMouseEvent>> pendingEvents(m_pendingMouseEvents); m_pendingMouseEvents.clear(); return pendingEvents; } @@ -90,14 +90,14 @@ bool PickEventFilter::eventFilter(QObject *obj, QEvent *e) case QEvent::MouseButtonRelease: case QEvent::MouseMove: { QMutexLocker locker(&m_mutex); - m_pendingMouseEvents.push_back(QMouseEvent(*static_cast<QMouseEvent *>(e))); + m_pendingMouseEvents.push_back({obj, QMouseEvent(*static_cast<QMouseEvent *>(e))}); } break; case QEvent::HoverMove: { QMutexLocker locker(&m_mutex); QHoverEvent *he = static_cast<QHoverEvent *>(e); - m_pendingMouseEvents.push_back(QMouseEvent(QEvent::MouseMove, + m_pendingMouseEvents.push_back({obj, QMouseEvent(QEvent::MouseMove, he->pos(), Qt::NoButton, Qt::NoButton, - he->modifiers())); + he->modifiers())}); } break; case QEvent::KeyPress: case QEvent::KeyRelease: { diff --git a/src/render/picking/pickeventfilter_p.h b/src/render/picking/pickeventfilter_p.h index fc4b00ddc..da41598b3 100644 --- a/src/render/picking/pickeventfilter_p.h +++ b/src/render/picking/pickeventfilter_p.h @@ -69,14 +69,14 @@ public: explicit PickEventFilter(QObject *parent = nullptr); ~PickEventFilter(); - QList<QMouseEvent> pendingMouseEvents(); + QList<QPair<QObject*, QMouseEvent>> pendingMouseEvents(); QList<QKeyEvent> pendingKeyEvents(); protected: bool eventFilter(QObject *obj, QEvent *e) Q_DECL_FINAL; private: - QList<QMouseEvent> m_pendingMouseEvents; + QList<QPair<QObject*, QMouseEvent>> m_pendingMouseEvents; QList<QKeyEvent> m_pendingKeyEvents; QMutex m_mutex; }; diff --git a/src/render/raycasting/qray3d_p.h b/src/render/raycasting/qray3d_p.h index c5e2be078..8b7852cc3 100644 --- a/src/render/raycasting/qray3d_p.h +++ b/src/render/raycasting/qray3d_p.h @@ -93,6 +93,8 @@ public: bool operator==(const QRay3D &other) const; bool operator!=(const QRay3D &other) const; + bool isValid() const { return !m_direction.isNull() && !qFuzzyIsNull(m_distance); } + private: Vector3D m_origin; Vector3D m_direction; diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp index 49618821c..4998e07d7 100644 --- a/tests/auto/core/nodes/tst_nodes.cpp +++ b/tests/auto/core/nodes/tst_nodes.cpp @@ -81,6 +81,7 @@ private slots: void checkConstructionSetParentMix(); // QTBUG-60612 void checkConstructionWithParent(); + void checkConstructionAsListElement(); void appendingComponentToEntity(); void appendingParentlessComponentToEntity(); @@ -240,6 +241,43 @@ public slots: emit nodePropertyChanged(node); } + void addAttribute(MyQNode *attribute) + { + Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(this); + if (!m_attributes.contains(attribute)) { + m_attributes.append(attribute); + + // Ensures proper bookkeeping + d->registerDestructionHelper(attribute, &MyQNode::removeAttribute, m_attributes); + + // We need to add it as a child of the current node if it has been declared inline + // Or not previously added as a child of the current node so that + // 1) The backend gets notified about it's creation + // 2) When the current node is destroyed, it gets destroyed as well + if (!attribute->parent()) + attribute->setParent(this); + + if (d->m_changeArbiter != nullptr) { + const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), attribute); + change->setPropertyName("attribute"); + d->notifyObservers(change); + } + } + } + + void removeAttribute(MyQNode *attribute) + { + Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(this); + if (d->m_changeArbiter != nullptr) { + const auto change = Qt3DCore::QPropertyNodeRemovedChangePtr::create(id(), attribute); + change->setPropertyName("attribute"); + d->notifyObservers(change); + } + m_attributes.removeOne(attribute); + // Remove bookkeeping connection + d->unregisterDestructionHelper(attribute); + } + signals: void customPropertyChanged(); void nodePropertyChanged(MyQNode *node); @@ -247,6 +285,7 @@ signals: protected: QString m_customProperty; MyQNode *m_nodeProperty; + QVector<MyQNode *> m_attributes; }; class MyQEntity : public Qt3DCore::QEntity @@ -921,6 +960,50 @@ void tst_Nodes::checkConstructionWithParent() QCOMPARE(propertyEvent->value().value<Qt3DCore::QNodeId>(), node->id()); } +void tst_Nodes::checkConstructionAsListElement() +{ + // GIVEN + ObserverSpy spy; + Qt3DCore::QScene scene; + QScopedPointer<MyQNode> root(new MyQNode()); + + // WHEN + root->setArbiterAndScene(&spy, &scene); + root->setSimulateBackendCreated(true); + + // THEN + QVERIFY(Qt3DCore::QNodePrivate::get(root.data())->scene() != nullptr); + + // WHEN we create a child and then set it as a Node* property + auto *node = new MyQNode(root.data()); + root->addAttribute(node); + + // THEN we should get one creation change, one child added change + // and one property change event, in that order. + QCoreApplication::processEvents(); + + QCOMPARE(root->children().count(), 1); + QCOMPARE(spy.events.size(), 3); // 1 creation change, 1 child added change, 1 property change + + // Ensure first event is child node's creation change + const auto creationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!creationEvent.isNull()); + QCOMPARE(creationEvent->subjectId(), node->id()); + + const auto newChildEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!newChildEvent.isNull()); + QCOMPARE(newChildEvent->subjectId(), root->id()); + QCOMPARE(newChildEvent->propertyName(), "children"); + QCOMPARE(newChildEvent->addedNodeId(), node->id()); + + // Ensure second and last event is property set change + const auto propertyEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!propertyEvent.isNull()); + QCOMPARE(propertyEvent->subjectId(), root->id()); + QCOMPARE(propertyEvent->propertyName(), "attribute"); + QCOMPARE(newChildEvent->addedNodeId(), node->id()); +} + void tst_Nodes::appendingParentlessComponentToEntity() { // GIVEN @@ -958,15 +1041,22 @@ void tst_Nodes::appendingParentlessComponentToEntity() // return early in such a case. // Check that we received ComponentAdded - for (const auto event: { entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(), - componentSpy.events.takeLast().change().dynamicCast<Qt3DCore::QComponentAddedChange>() }) { + const auto event = entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); QCOMPARE(event->type(), Qt3DCore::ComponentAdded); QCOMPARE(event->subjectId(), entity->id()); QCOMPARE(event->entityId(), entity->id()); QCOMPARE(event->componentId(), comp->id()); QCOMPARE(event->componentMetaObject(), comp->metaObject()); } + { + const auto event = componentSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } } } @@ -997,15 +1087,22 @@ void tst_Nodes::appendingComponentToEntity() QVERIFY(comp->parentNode() == entity.data()); QCOMPARE(entitySpy.events.size(), 1); QVERIFY(entitySpy.events.first().wasLocked()); - for (const auto event: { entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(), - componentSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>() }) { + const auto event = entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); QCOMPARE(event->type(), Qt3DCore::ComponentAdded); QCOMPARE(event->subjectId(), entity->id()); QCOMPARE(event->entityId(), entity->id()); QCOMPARE(event->componentId(), comp->id()); QCOMPARE(event->componentMetaObject(), comp->metaObject()); } + { + const auto event = componentSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } } } @@ -1040,14 +1137,22 @@ void tst_Nodes::removingComponentFromEntity() QCOMPARE(entitySpy.events.size(), 1); QVERIFY(entitySpy.events.first().wasLocked()); QCOMPARE(componentSpy.events.size(), 1); - for (const auto event: { entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentRemovedChange>(), - componentSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentRemovedChange>() }) { + { + const auto event = entitySpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentRemovedChange>(); QCOMPARE(event->type(), Qt3DCore::ComponentRemoved); QCOMPARE(event->subjectId(), entity->id()); QCOMPARE(event->entityId(), entity->id()); QCOMPARE(event->componentId(), comp->id()); QCOMPARE(event->componentMetaObject(), comp->metaObject()); } + { + const auto event = componentSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentRemovedChange>(); + QCOMPARE(event->type(), Qt3DCore::ComponentRemoved); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } } } diff --git a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp index fcbfaf6ba..620c74641 100644 --- a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp +++ b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp @@ -65,6 +65,8 @@ #include <Qt3DExtras/qcuboidmesh.h> #include <Qt3DExtras/qplanemesh.h> +#include <qbackendnodetester.h> + QT_BEGIN_NAMESPACE namespace Qt3DRender { @@ -100,6 +102,7 @@ public: Qt3DRender::Render::FrameGraphNode *frameGraphRoot() const { return d_func()->m_renderer->frameGraphRoot(); } Qt3DRender::Render::RenderSettings *renderSettings() const { return d_func()->m_renderer->settings(); } Qt3DRender::Render::Entity *sceneRoot() const { return m_sceneRoot; } + Qt3DRender::Render::AbstractRenderer *renderer() const { return d_func()->m_renderer; } private: Render::Entity *m_sceneRoot; @@ -153,7 +156,7 @@ void runRequiredJobs(Qt3DRender::TestAspect *test) } // anonymous -class tst_BoundingSphere : public QObject +class tst_BoundingSphere : public Qt3DCore::QBackendNodeTester { Q_OBJECT private: @@ -194,6 +197,229 @@ private Q_SLOTS: QVERIFY(qAbs(boundingSphere->center().y() - sphereCenter.y()) < 0.000001f); QVERIFY(qAbs(boundingSphere->center().z() - sphereCenter.z()) < 0.000001f); } + + void checkCustomGeometry_data() + { + QTest::addColumn<int>("drawVertexCount"); + QTest::addColumn<int>("indexByteOffset"); + QTest::addColumn<QVector3D>("expectedCenter"); + QTest::addColumn<float>("expectedRadius"); + QTest::newRow("all") << 0 << 0 << QVector3D(-0.488892f, 0.0192147f, -75.4804f) << 25.5442f; + QTest::newRow("first only") << 3 << 0 << QVector3D(0, 1, -100) << 1.0f; + QTest::newRow("second only") << 3 << int(3 * sizeof(ushort)) << QVector3D(0, -1, -50) << 1.0f; + } + + void checkCustomGeometry() + { + QFETCH(int, drawVertexCount); + QFETCH(int, indexByteOffset); + QFETCH(QVector3D, expectedCenter); + QFETCH(float, expectedRadius); + + // two triangles with different Z, and an index buffer + QByteArray vdata; + vdata.resize(6 * 3 * sizeof(float)); + float *vp = reinterpret_cast<float *>(vdata.data()); + *vp++ = -1.0f; + *vp++ = 1.0f; + *vp++ = -100.0f; + *vp++ = 0.0f; + *vp++ = 0.0f; + *vp++ = -100.0f; + *vp++ = 1.0f; + *vp++ = 1.0f; + *vp++ = -100.0f; + + *vp++ = -1.0f; + *vp++ = -1.0f; + *vp++ = -50.0f; + *vp++ = 0.0f; + *vp++ = 0.0f; + *vp++ = -50.0f; + *vp++ = 1.0f; + *vp++ = -1.0f; + *vp++ = -50.0f; + + QByteArray idata; + idata.resize(6 * sizeof(ushort)); + ushort *ip = reinterpret_cast<ushort *>(idata.data()); + *ip++ = 0; + *ip++ = 1; + *ip++ = 2; + *ip++ = 3; + *ip++ = 4; + *ip++ = 5; + + QScopedPointer<Qt3DCore::QEntity> entity(new Qt3DCore::QEntity); + QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(entity.data())); + Qt3DRender::QBuffer *vbuffer = new Qt3DRender::QBuffer; + Qt3DRender::QBuffer *ibuffer = new Qt3DRender::QBuffer; + + vbuffer->setData(vdata); + Qt3DRender::Render::Buffer *vbufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(vbuffer->id()); + vbufferBackend->setRenderer(test->renderer()); + vbufferBackend->setManager(test->nodeManagers()->bufferManager()); + simulateInitialization(vbuffer, vbufferBackend); + + ibuffer->setData(idata); + Qt3DRender::Render::Buffer *ibufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(ibuffer->id()); + ibufferBackend->setRenderer(test->renderer()); + ibufferBackend->setManager(test->nodeManagers()->bufferManager()); + simulateInitialization(ibuffer, ibufferBackend); + + Qt3DRender::QGeometry *g = new Qt3DRender::QGeometry; + for (int i = 0; i < 2; ++i) + g->addAttribute(new Qt3DRender::QAttribute); + + const QVector<Qt3DRender::QAttribute *> attrs = g->attributes(); + Qt3DRender::QAttribute *attr = attrs[0]; + attr->setBuffer(vbuffer); + attr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); + attr->setVertexBaseType(Qt3DRender::QAttribute::Float); + attr->setVertexSize(3); + attr->setCount(6); + attr->setByteOffset(0); + attr->setByteStride(3 * sizeof(float)); + + attr = attrs[1]; + attr->setBuffer(ibuffer); + attr->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); + attr->setVertexBaseType(Qt3DRender::QAttribute::UnsignedShort); + attr->setVertexSize(1); + attr->setCount(6); + attr->setByteOffset(indexByteOffset); + + Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer; + gr->setVertexCount(drawVertexCount); // when 0, indexAttribute->count() is used instead + gr->setGeometry(g); + entity->addComponent(gr); + + Qt3DRender::Render::Attribute *attr0Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(attrs[0]->id()); + attr0Backend->setRenderer(test->renderer()); + simulateInitialization(attrs[0], attr0Backend); + Qt3DRender::Render::Attribute *attr1Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(attrs[1]->id()); + attr1Backend->setRenderer(test->renderer()); + simulateInitialization(attrs[1], attr1Backend); + + Qt3DRender::Render::Geometry *gBackend = test->nodeManagers()->geometryManager()->getOrCreateResource(g->id()); + gBackend->setRenderer(test->renderer()); + simulateInitialization(g, gBackend); + + Qt3DRender::Render::GeometryRenderer *grBackend = test->nodeManagers()->geometryRendererManager()->getOrCreateResource(gr->id()); + grBackend->setRenderer(test->renderer()); + grBackend->setManager(test->nodeManagers()->geometryRendererManager()); + simulateInitialization(gr, grBackend); + + Qt3DRender::Render::Entity *entityBackend = test->nodeManagers()->renderNodesManager()->getOrCreateResource(entity->id()); + entityBackend->setRenderer(test->renderer()); + simulateInitialization(entity.data(), entityBackend); + + Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume; + calcBVolume.setManagers(test->nodeManagers()); + calcBVolume.setRoot(test->sceneRoot()); + calcBVolume.run(); + + Vector3D center = entityBackend->localBoundingVolume()->center(); + float radius = entityBackend->localBoundingVolume()->radius(); + qDebug() << radius << center; + + // truncate and compare integers only + QVERIFY(int(radius) == int(expectedRadius)); + QVERIFY(int(center.x()) == int(expectedCenter.x())); + QVERIFY(int(center.y()) == int(expectedCenter.y())); + QVERIFY(int(center.z()) == int(expectedCenter.z())); + } + + void checkCustomPackedGeometry() + { + int drawVertexCount = 6; + QVector3D expectedCenter(-0.488892f, 0.0192147f, -75.4804f); + float expectedRadius = 25.5442f; + + // two triangles with different Z + QByteArray vdata; + vdata.resize(6 * 3 * sizeof(float)); + float *vp = reinterpret_cast<float *>(vdata.data()); + *vp++ = -1.0f; + *vp++ = 1.0f; + *vp++ = -100.0f; + *vp++ = 0.0f; + *vp++ = 0.0f; + *vp++ = -100.0f; + *vp++ = 1.0f; + *vp++ = 1.0f; + *vp++ = -100.0f; + + *vp++ = -1.0f; + *vp++ = -1.0f; + *vp++ = -50.0f; + *vp++ = 0.0f; + *vp++ = 0.0f; + *vp++ = -50.0f; + *vp++ = 1.0f; + *vp++ = -1.0f; + *vp++ = -50.0f; + + QScopedPointer<Qt3DCore::QEntity> entity(new Qt3DCore::QEntity); + QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(entity.data())); + Qt3DRender::QBuffer *vbuffer = new Qt3DRender::QBuffer; + + vbuffer->setData(vdata); + Qt3DRender::Render::Buffer *vbufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(vbuffer->id()); + vbufferBackend->setRenderer(test->renderer()); + vbufferBackend->setManager(test->nodeManagers()->bufferManager()); + simulateInitialization(vbuffer, vbufferBackend); + + Qt3DRender::QGeometry *g = new Qt3DRender::QGeometry; + g->addAttribute(new Qt3DRender::QAttribute); + + const QVector<Qt3DRender::QAttribute *> attrs = g->attributes(); + Qt3DRender::QAttribute *attr = attrs[0]; + attr->setBuffer(vbuffer); + attr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); + attr->setVertexBaseType(Qt3DRender::QAttribute::Float); + attr->setVertexSize(3); + attr->setCount(6); + attr->setByteOffset(0); + attr->setByteStride(0); + + Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer; + gr->setVertexCount(drawVertexCount); + gr->setGeometry(g); + entity->addComponent(gr); + + Qt3DRender::Render::Attribute *attr0Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(attrs[0]->id()); + attr0Backend->setRenderer(test->renderer()); + simulateInitialization(attrs[0], attr0Backend); + + Qt3DRender::Render::Geometry *gBackend = test->nodeManagers()->geometryManager()->getOrCreateResource(g->id()); + gBackend->setRenderer(test->renderer()); + simulateInitialization(g, gBackend); + + Qt3DRender::Render::GeometryRenderer *grBackend = test->nodeManagers()->geometryRendererManager()->getOrCreateResource(gr->id()); + grBackend->setRenderer(test->renderer()); + grBackend->setManager(test->nodeManagers()->geometryRendererManager()); + simulateInitialization(gr, grBackend); + + Qt3DRender::Render::Entity *entityBackend = test->nodeManagers()->renderNodesManager()->getOrCreateResource(entity->id()); + entityBackend->setRenderer(test->renderer()); + simulateInitialization(entity.data(), entityBackend); + + Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume; + calcBVolume.setManagers(test->nodeManagers()); + calcBVolume.setRoot(test->sceneRoot()); + calcBVolume.run(); + + Vector3D center = entityBackend->localBoundingVolume()->center(); + float radius = entityBackend->localBoundingVolume()->radius(); + qDebug() << radius << center; + + // truncate and compare integers only + QVERIFY(int(radius) == int(expectedRadius)); + QVERIFY(int(center.x()) == int(expectedCenter.x())); + QVERIFY(int(center.y()) == int(expectedCenter.y())); + QVERIFY(int(center.z()) == int(expectedCenter.z())); + } }; QTEST_MAIN(tst_BoundingSphere) diff --git a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc index c2b0c7fff..feef480e2 100644 --- a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc +++ b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc @@ -8,5 +8,7 @@ <file>testscene_pickersdisabled.qml</file> <file>testscene_dragenabledoverlapping.qml</file> <file>testscene_parententity.qml</file> + <file>testscene_viewports.qml</file> + <file>testscene_cameraposition.qml</file> </qresource> </RCC> diff --git a/tests/auto/render/pickboundingvolumejob/testscene_cameraposition.qml b/tests/auto/render/pickboundingvolumejob/testscene_cameraposition.qml new file mode 100644 index 000000000..87e7a8aac --- /dev/null +++ b/tests/auto/render/pickboundingvolumejob/testscene_cameraposition.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Extras 2.0 +import QtQuick.Window 2.0 + +Entity { + id: sceneRoot + + Window { + id: win + width: 600 + height: 600 + visible: true + } + + Camera { + id: camera + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + nearPlane : 0.1 + farPlane : 1000.0 + position: Qt.vector3d( 0.0, 0.0, -40.0 ) + upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) + viewCenter: Qt.vector3d( 5.0, 3.0, 1.0 ) + } + + components: [ + RenderSettings { + Viewport { + normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0) + + RenderSurfaceSelector { + + surface: win + + ClearBuffers { + buffers : ClearBuffers.ColorDepthBuffer + NoDraw {} + } + + CameraSelector { + camera: camera + } + } + } + } + ] + + CuboidMesh { id: cubeMesh } + PhongMaterial { id: material } + + Entity { + property ObjectPicker picker: ObjectPicker { + objectName: "Picker" + } + + property Transform transform: Transform { + translation: camera.viewCenter + scale: 2.0 + } + + components: [cubeMesh, material, picker, transform] + } + +} diff --git a/tests/auto/render/pickboundingvolumejob/testscene_viewports.qml b/tests/auto/render/pickboundingvolumejob/testscene_viewports.qml new file mode 100644 index 000000000..daafc0edd --- /dev/null +++ b/tests/auto/render/pickboundingvolumejob/testscene_viewports.qml @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Extras 2.0 +import QtQuick.Window 2.0 + +Entity { + id: sceneRoot + + Window { + id: _view + width: 600 + height: 600 + visible: true + } + + Camera { + id: camera + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: _view.width / 2 / _view.height + nearPlane : 0.1 + farPlane : 1000.0 + position: Qt.vector3d( 0.0, 0.0, -10.0 ) + upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) + viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 ) + } + + Camera { + id: camera2 + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: _view.width / _view.height + nearPlane : 0.1 + farPlane : 1000.0 + position: Qt.vector3d( 0.0, 0.0, -20.0 ) + upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) + viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 ) + } + + FirstPersonCameraController { + camera: camera + } + + DirectionalLight { + worldDirection: camera.viewVector.times(-1) + } + + // Draw 2 viewports + // one with the content, the other with content + debug volumes + components: [ + RenderSettings { + Viewport { + normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0) + + RenderSurfaceSelector { + surface: _view + + Viewport { + normalizedRect: Qt.rect(0.0, 0.0, 0.5, 1.0) + ClearBuffers { + buffers : ClearBuffers.ColorDepthBuffer + clearColor: "white" + CameraSelector { + camera: camera + } + } + } + + Viewport { + normalizedRect: Qt.rect(0.5, 0.0, 0.5, 1.0) + CameraSelector { + camera: camera2 + } + } + } + } + } + ] + + CuboidMesh { id: cubeMesh } + + Entity { + readonly property ObjectPicker objectPicker: ObjectPicker { + onClicked: console.log("o1") + } + readonly property Transform transform: Transform { + scale: 3 + translation: Qt.vector3d(3, 0, 0) + } + readonly property PhongMaterial material: PhongMaterial { diffuse: "red" } + + components: [cubeMesh, transform, material, objectPicker ] + } + + Entity { + readonly property ObjectPicker objectPicker: ObjectPicker { + objectName: "Picker2" + onClicked: console.log("o2") + } + readonly property Transform transform: Transform { + scale: 3 + translation: Qt.vector3d(-3, 0, 0) + } + readonly property PhongMaterial material: PhongMaterial { diffuse: "green" } + + components: [cubeMesh, transform, material, objectPicker ] + } +} diff --git a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp index a14bccefc..b86df05a4 100644 --- a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp +++ b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp @@ -234,7 +234,7 @@ private Q_SLOTS: // WHEN Qt3DRender::Render::PickingUtils::ViewportCameraAreaGatherer gatherer; - QVector<Qt3DRender::Render::PickingUtils::ViewportCameraAreaTriplet> results = gatherer.gather(test->frameGraphRoot()); + QVector<Qt3DRender::Render::PickingUtils::ViewportCameraAreaDetails> results = gatherer.gather(test->frameGraphRoot()); // THEN QCOMPARE(results.size(), 1); @@ -329,8 +329,8 @@ private Q_SLOTS: QVERIFY(pickBVJob.currentPicker().isNull()); // WHEN - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *,QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -343,7 +343,7 @@ private Q_SLOTS: // WHEN events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -353,7 +353,7 @@ private Q_SLOTS: // WHEN events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(390.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(390.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -366,7 +366,7 @@ private Q_SLOTS: // WHEN events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(390.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(390.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -425,8 +425,9 @@ private Q_SLOTS: QVERIFY(earlyReturn); // WHEN - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 440.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 440.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -474,8 +475,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); // THEN @@ -530,9 +532,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), - Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -580,8 +582,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -591,7 +594,8 @@ private Q_SLOTS: // WHEN events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -639,8 +643,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -650,7 +655,8 @@ private Q_SLOTS: // WHEN events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -698,8 +704,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); const bool earlyReturn = !pickBVJob.runHelper(); @@ -764,8 +771,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -785,7 +793,8 @@ private Q_SLOTS: // WHEN -> Move on same object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -805,7 +814,8 @@ private Q_SLOTS: // WHEN -> Release on object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -832,8 +842,10 @@ private Q_SLOTS: // WHEN -> Release outside of object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -900,8 +912,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::HoverMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::HoverMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -916,7 +929,8 @@ private Q_SLOTS: // WHEN -> HoverMove Out events.clear(); - events.push_back(QMouseEvent(QEvent::HoverMove, QPointF(20.0f, 40.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QEvent::HoverMove, QPointF(20.0f, 40.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -931,8 +945,10 @@ private Q_SLOTS: // WHEN -> HoverMove In + Pressed other events.clear(); - events.push_back(QMouseEvent(QEvent::HoverMove, QPointF(207.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); - events.push_back(QMouseEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QEvent::HoverMove, QPointF(207.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + events.push_back({nullptr, QMouseEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -1001,8 +1017,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -1017,7 +1034,8 @@ private Q_SLOTS: // WHEN -> Move on same object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(400.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(400.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -1032,7 +1050,8 @@ private Q_SLOTS: // WHEN -> Release on object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(400.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(400.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -1049,8 +1068,10 @@ private Q_SLOTS: // WHEN -> Release outside of object events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); - events.push_back(QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -1126,8 +1147,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(320.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(320.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -1141,7 +1163,8 @@ private Q_SLOTS: // WHEN -> Move on next object, show stay on previous picker unless all picks are requested events.clear(); - events.push_back(QMouseEvent(QMouseEvent::MouseMove, QPointF(280.0f, 303.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseMove, QPointF(280.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); earlyReturn = !pickBVJob.runHelper(); @@ -1200,8 +1223,9 @@ private Q_SLOTS: Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; initializePickBoundingVolumeJob(&pickBVJob, test.data()); - QList<QMouseEvent> events; - events.push_back(QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(400.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); pickBVJob.setMouseEvents(events); bool earlyReturn = !pickBVJob.runHelper(); @@ -1212,6 +1236,155 @@ private Q_SLOTS: Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>(); QCOMPARE(change->propertyName(), "pressed"); } + + void checkPickerAndViewports() + { + // GIVEN + QmlSceneReader sceneReader(QUrl("qrc:/testscene_viewports.qml")); + QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(sceneReader.root())); + QVERIFY(root); + + QList<Qt3DRender::QRenderSettings *> renderSettings = root->findChildren<Qt3DRender::QRenderSettings *>(); + QCOMPARE(renderSettings.size(), 1); + Qt3DRender::QPickingSettings *settings = renderSettings.first()->pickingSettings(); + + settings->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking); + settings->setPickResultMode(Qt3DRender::QPickingSettings::NearestPick); + settings->setFaceOrientationPickingMode(Qt3DRender::QPickingSettings::FrontFace); + + QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data())); + TestArbiter arbiter; + + // Runs Required jobs + runRequiredJobs(test.data()); + + // THEN + // object partially obscured by another viewport, make sure only visible portion is pickable + QList<Qt3DRender::QObjectPicker *> pickers = root->findChildren<Qt3DRender::QObjectPicker *>(); + QCOMPARE(pickers.size(), 2); + + Qt3DRender::QObjectPicker *picker = pickers.last(); + QCOMPARE(picker->objectName(), QLatin1String("Picker2")); + + Qt3DRender::Render::ObjectPicker *backendPicker = test->nodeManagers()->objectPickerManager()->lookupResource(picker->id()); + QVERIFY(backendPicker); + Qt3DCore::QBackendNodePrivate::get(backendPicker)->setArbiter(&arbiter); + + // WHEN -> Pressed on object in vp1 + Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; + initializePickBoundingVolumeJob(&pickBVJob, test.data()); + + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(280.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + pickBVJob.setMouseEvents(events); + bool earlyReturn = !pickBVJob.runHelper(); + + // THEN -> Pressed + QVERIFY(!earlyReturn); + QVERIFY(backendPicker->isPressed()); + QCOMPARE(arbiter.events.count(), 1); + Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>(); + QCOMPARE(change->propertyName(), "pressed"); + + // WHEN reset -> Presset on object in vp2 + backendPicker->cleanup(); + backendPicker->setEnabled(true); + events.clear(); + arbiter.events.clear(); + + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(320.0f, 300.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + pickBVJob.setMouseEvents(events); + earlyReturn = !pickBVJob.runHelper(); + + // THEN -> Nothing happened + QVERIFY(!earlyReturn); + QVERIFY(!backendPicker->isPressed()); + QCOMPARE(arbiter.events.count(), 0); + } + + void checkMultipleRayDirections_data() + { + QTest::addColumn<QVector3D>("cameraOrigin"); + QTest::addColumn<QVector3D>("cameraUpVector"); + + int k = 0; + const int n = 10; + for (int j=0; j<n; j++) { + QMatrix4x4 m; + m.rotate(360.f / (float)n * (float)j, 0.f, 0.f, 1.f); + for (int i=0; i<n; i++) { + const double angle = M_PI * 2. / (double)n * i; + const double x = std::sin(angle) * 10.; + const double z = std::cos(angle) * 10.; + QVector3D pos(x, 0, z); + QVector3D up(0, 1, 0); + QTest::newRow(QString::number(k++).toLatin1().data()) << m * pos << m * up; + } + } + } + + void checkMultipleRayDirections() + { + // GIVEN + QmlSceneReader sceneReader(QUrl("qrc:/testscene_cameraposition.qml")); + QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(sceneReader.root())); + QVERIFY(root); + + QList<Qt3DRender::QRenderSettings *> renderSettings = root->findChildren<Qt3DRender::QRenderSettings *>(); + QCOMPARE(renderSettings.size(), 1); + Qt3DRender::QPickingSettings *settings = renderSettings.first()->pickingSettings(); + + settings->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking); + + QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data())); + TestArbiter arbiter; + + QList<Qt3DRender::QCamera *> cameras = root->findChildren<Qt3DRender::QCamera *>(); + QCOMPARE(cameras.size(), 1); + Qt3DRender::QCamera *camera = cameras.first(); + + QFETCH(QVector3D, cameraUpVector); + camera->setUpVector(cameraUpVector); + + QFETCH(QVector3D, cameraOrigin); + camera->setPosition(cameraOrigin); + + // Runs Required jobs + runRequiredJobs(test.data()); + + // THEN + QList<Qt3DRender::QObjectPicker *> pickers = root->findChildren<Qt3DRender::QObjectPicker *>(); + QCOMPARE(pickers.size(), 1); + + Qt3DRender::QObjectPicker *picker = pickers.front(); + + Qt3DRender::Render::ObjectPicker *backendPicker = test->nodeManagers()->objectPickerManager()->lookupResource(picker->id()); + QVERIFY(backendPicker); + Qt3DCore::QBackendNodePrivate::get(backendPicker)->setArbiter(&arbiter); + + // WHEN -> Pressed on object + Qt3DRender::Render::PickBoundingVolumeJob pickBVJob; + initializePickBoundingVolumeJob(&pickBVJob, test.data()); + + QList<QPair<QObject *, QMouseEvent>> events; + events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(303.0f, 303.0f), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)}); + pickBVJob.setMouseEvents(events); + bool earlyReturn = !pickBVJob.runHelper(); + + // THEN -> Pressed + QVERIFY(!earlyReturn); + QVERIFY(backendPicker->isPressed()); + Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>(); + QCOMPARE(change->propertyName(), "pressed"); + Qt3DRender::QPickEventPtr pickEvent = change->value().value<Qt3DRender::QPickEventPtr>(); + QVERIFY(pickEvent); + + arbiter.events.clear(); + } + }; QTEST_MAIN(tst_PickBoundingVolumeJob) diff --git a/tests/auto/render/shadercache/tst_shadercache.cpp b/tests/auto/render/shadercache/tst_shadercache.cpp index 1c70d4405..49628ef0f 100644 --- a/tests/auto/render/shadercache/tst_shadercache.cpp +++ b/tests/auto/render/shadercache/tst_shadercache.cpp @@ -131,6 +131,19 @@ void tst_ShaderCache::value() auto dnaC = ProgramDNA(54321); auto uncachedProgram = cache.getShaderProgramAndAddRef(dnaC, nodeIdB); QVERIFY(uncachedProgram == nullptr); + + cache.clear(); + // Test inserting nullptr. + cache.insert(dnaA, nodeIdA, nullptr); + bool wasPresent = false; + cachedProgramA = cache.getShaderProgramAndAddRef(dnaA, nodeIdA, &wasPresent); + QCOMPARE(wasPresent, true); + QCOMPARE(cachedProgramA, nullptr); + cache.clear(); + // Test wasPresent==false. + cachedProgramB = cache.getShaderProgramAndAddRef(dnaB, nodeIdB, &wasPresent); + QCOMPARE(wasPresent, false); + QCOMPARE(cachedProgramB, nullptr); } void tst_ShaderCache::removeRef() diff --git a/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp b/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp index 6a9be5e13..4205d598e 100644 --- a/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp +++ b/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp @@ -194,7 +194,7 @@ private Q_SLOTS: positionAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); positionAttribute->setVertexSize(3); positionAttribute->setCount(6); - positionAttribute->setByteStride(3*4); + positionAttribute->setByteStride(0); positionAttribute->setByteOffset(0); positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); geometry->addAttribute(positionAttribute.data()); |