summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/changes-5.12.320
-rw-r--r--examples/examples.pro3
-rw-r--r--qt3d.pro4
-rw-r--r--src/animation/backend/animationutils.cpp118
-rw-r--r--src/animation/backend/animationutils_p.h37
-rw-r--r--src/animation/backend/channelmapping.cpp5
-rw-r--r--src/animation/backend/channelmapping_p.h4
-rw-r--r--src/animation/frontend/qabstractchannelmapping_p.h2
-rw-r--r--src/animation/frontend/qchannelmapping.cpp78
-rw-r--r--src/animation/frontend/qchannelmapping_p.h10
-rw-r--r--src/core/changes/qpropertynodeaddedchange.cpp2
-rw-r--r--src/core/configure.json4
-rw-r--r--src/core/nodes/qentity.cpp15
-rw-r--r--src/core/nodes/qentity.h3
-rw-r--r--src/core/nodes/qnode.cpp79
-rw-r--r--src/core/nodes/qnode_p.h18
-rw-r--r--src/core/qscene.cpp8
-rw-r--r--src/core/qscene_p.h3
-rw-r--r--src/quick3d/quick3d/items/quick3dentityloader.cpp3
-rw-r--r--src/render/backend/abstractrenderer_p.h3
-rw-r--r--src/render/backend/entity.cpp74
-rw-r--r--src/render/backend/entity_p.h6
-rw-r--r--src/render/backend/managers.cpp6
-rw-r--r--src/render/configure.json4
-rw-r--r--src/render/framegraph/framegraphnode.cpp52
-rw-r--r--src/render/framegraph/framegraphnode_p.h4
-rw-r--r--src/render/framegraph/qframegraphnode.cpp16
-rw-r--r--src/render/framegraph/qframegraphnode.h3
-rw-r--r--src/render/jobs/job_common_p.h1
-rw-r--r--src/render/jobs/jobs.pri2
-rw-r--r--src/render/jobs/updateentityhierarchyjob.cpp80
-rw-r--r--src/render/jobs/updateentityhierarchyjob_p.h91
-rw-r--r--src/render/jobs/updateentitylayersjob_p.h2
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp23
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h2
-rw-r--r--src/src.pro3
-rw-r--r--tests/auto/animation/animation.pro4
-rw-r--r--tests/auto/animation/animationutils/tst_animationutils.cpp367
-rw-r--r--tests/auto/animation/channelmapping/tst_channelmapping.cpp53
-rw-r--r--tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp16
-rw-r--r--tests/auto/animation/qchannelmapping/tst_qchannelmapping.cpp161
-rw-r--r--tests/auto/core/nodes/tst_nodes.cpp147
-rw-r--r--tests/auto/render/boundingsphere/tst_boundingsphere.cpp5
-rw-r--r--tests/auto/render/entity/tst_entity.cpp119
-rw-r--r--tests/auto/render/framegraphnode/tst_framegraphnode.cpp52
-rw-r--r--tests/auto/render/layerfiltering/tst_layerfiltering.cpp6
-rw-r--r--tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp5
-rw-r--r--tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp5
-rw-r--r--tests/auto/render/qcamera/tst_qcamera.cpp5
-rw-r--r--tests/auto/render/raycastingjob/tst_raycastingjob.cpp5
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp31
-rw-r--r--tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp3
-rw-r--r--tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp5
-rw-r--r--tests/manual/cylinder-parent-test/cylinder-parent-test.pro9
-rw-r--r--tests/manual/cylinder-parent-test/main.cpp243
-rw-r--r--tests/manual/manual.pro1
-rw-r--r--tests/tests.pro3
-rw-r--r--tools/tools.pro4
58 files changed, 1726 insertions, 311 deletions
diff --git a/dist/changes-5.12.3 b/dist/changes-5.12.3
new file mode 100644
index 000000000..88033201a
--- /dev/null
+++ b/dist/changes-5.12.3
@@ -0,0 +1,20 @@
+Qt 5.12.3 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.2.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/examples/examples.pro b/examples/examples.pro
index 6a3c01d76..037f14609 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -1,5 +1,8 @@
TEMPLATE = subdirs
+!qtHaveModule(3dcore): \
+ return()
+
QT_FOR_CONFIG += 3dcore
qtConfig(qt3d-extras): SUBDIRS += qt3d
diff --git a/qt3d.pro b/qt3d.pro
index 847507f17..d5dbda4b9 100644
--- a/qt3d.pro
+++ b/qt3d.pro
@@ -1,5 +1,5 @@
-requires(qtHaveModule(gui))
-requires(qtConfig(opengl):!wasm)
+qtHaveModule(gui): \
+ requires(qtConfig(opengl):!wasm)
CONFIG += examples_need_tools
load(qt_parts)
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index c876fcb87..17826e946 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -58,36 +58,6 @@ QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
namespace Animation {
-int componentsForType(int type)
-{
- int componentCount = 1;
- switch (type) {
- case QMetaType::Float:
- case QVariant::Double:
- componentCount = 1;
- break;
-
- case QVariant::Vector2D:
- componentCount = 2;
- break;
-
- case QVariant::Vector3D:
- case QVariant::Color:
- componentCount = 3;
- break;
-
- case QVariant::Vector4D:
- case QVariant::Quaternion:
- componentCount = 4;
- break;
-
- default:
- qWarning() << "Unhandled animation type";
- }
-
- return componentCount;
-}
-
inline QVector<float> valueToVector(const QVector3D &value)
{
return { value.x(), value.y(), value.z() };
@@ -178,7 +148,10 @@ double phaseFromElapsedTime(double t_current_local,
later be used as part of the format vector in the formatClipResults() function to
remap the channels into the standard W, X, Y, Z order required by QQuaternion.
*/
-ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType, int offset)
+ComponentIndices channelComponentsToIndices(const Channel &channel,
+ int dataType,
+ int expectedComponentCount,
+ int offset)
{
#if defined Q_COMPILER_UNIFORM_INIT
static const QVector<char> standardSuffixes = { 'X', 'Y', 'Z', 'W' };
@@ -192,20 +165,22 @@ ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType
switch (dataType) {
case QVariant::Quaternion:
- return channelComponentsToIndicesHelper(channel, dataType, offset, quaternionSuffixes);
+ return channelComponentsToIndicesHelper(channel, expectedComponentCount,
+ offset, quaternionSuffixes);
case QVariant::Color:
- return channelComponentsToIndicesHelper(channel, dataType, offset, colorSuffixes);
+ return channelComponentsToIndicesHelper(channel, expectedComponentCount,
+ offset, colorSuffixes);
default:
- return channelComponentsToIndicesHelper(channel, dataType, offset, standardSuffixes);
+ return channelComponentsToIndicesHelper(channel, expectedComponentCount,
+ offset, standardSuffixes);
}
}
ComponentIndices channelComponentsToIndicesHelper(const Channel &channel,
- int dataType,
+ int expectedComponentCount,
int offset,
const QVector<char> &suffixes)
{
- const int expectedComponentCount = componentsForType(dataType);
const int actualComponentCount = channel.channelComponents.size();
if (actualComponentCount != expectedComponentCount) {
qWarning() << "Data type expects" << expectedComponentCount
@@ -299,10 +274,10 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime)
if (qFuzzyIsNull(omega)) {
// If the two keyframe quaternions are equal, just return the first one as the interpolated value.
- channelResults[0] = lowerQuat.scalar();
- channelResults[1] = lowerQuat.x();
- channelResults[2] = lowerQuat.y();
- channelResults[3] = lowerQuat.z();
+ channelResults[i++] = lowerQuat.scalar();
+ channelResults[i++] = lowerQuat.x();
+ channelResults[i++] = lowerQuat.y();
+ channelResults[i++] = lowerQuat.z();
} else {
for (const auto &channelComponent : qAsConst(channel.channelComponents))
channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega);
@@ -329,30 +304,44 @@ ClipResults evaluateClipAtPhase(AnimationClip *clip, float phase)
return evaluateClipAtLocalTime(clip, localTime);
}
+template<typename Container>
+Container mapChannelResultsToContainer(const MappingData &mappingData,
+ const QVector<float> &channelResults)
+{
+ Container r;
+ r.reserve(channelResults.size());
+
+ const ComponentIndices channelIndices = mappingData.channelIndices;
+ for (const int channelIndex : channelIndices)
+ r.push_back(channelResults.at(channelIndex));
+
+ return r;
+}
+
QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> &channelResults)
{
- QVariant v;
+ const int vectorOfFloatType = qMetaTypeId<QVector<float>>();
+
+ if (mappingData.type == vectorOfFloatType)
+ return QVariant::fromValue(channelResults);
switch (mappingData.type) {
case QMetaType::Float:
case QVariant::Double: {
- v = QVariant::fromValue(channelResults[mappingData.channelIndices[0]]);
- break;
+ return QVariant::fromValue(channelResults[mappingData.channelIndices[0]]);
}
case QVariant::Vector2D: {
const QVector2D vector(channelResults[mappingData.channelIndices[0]],
channelResults[mappingData.channelIndices[1]]);
- v = QVariant::fromValue(vector);
- break;
+ return QVariant::fromValue(vector);
}
case QVariant::Vector3D: {
const QVector3D vector(channelResults[mappingData.channelIndices[0]],
channelResults[mappingData.channelIndices[1]],
channelResults[mappingData.channelIndices[2]]);
- v = QVariant::fromValue(vector);
- break;
+ return QVariant::fromValue(vector);
}
case QVariant::Vector4D: {
@@ -360,8 +349,7 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float>
channelResults[mappingData.channelIndices[1]],
channelResults[mappingData.channelIndices[2]],
channelResults[mappingData.channelIndices[3]]);
- v = QVariant::fromValue(vector);
- break;
+ return QVariant::fromValue(vector);
}
case QVariant::Quaternion: {
@@ -370,24 +358,28 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float>
channelResults[mappingData.channelIndices[2]],
channelResults[mappingData.channelIndices[3]]);
q.normalize();
- v = QVariant::fromValue(q);
- break;
+ return QVariant::fromValue(q);
}
case QVariant::Color: {
- const QColor color = QColor::fromRgbF(channelResults[mappingData.channelIndices[0]],
+ const QColor color =
+ QColor::fromRgbF(channelResults[mappingData.channelIndices[0]],
channelResults[mappingData.channelIndices[1]],
channelResults[mappingData.channelIndices[2]]);
- v = QVariant::fromValue(color);
- break;
+ return QVariant::fromValue(color);
}
+ case QVariant::List: {
+ const QVariantList results = mapChannelResultsToContainer<QVariantList>(
+ mappingData, channelResults);
+ return QVariant::fromValue(results);
+ }
default:
qWarning() << "Unhandled animation type" << mappingData.type;
break;
}
- return v;
+ return QVariant();
}
QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId,
@@ -540,7 +532,11 @@ QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping*> &chann
}
// Try to find matching channel name and type
- const ChannelNameAndType nameAndType = { mapping->channelName(), mapping->type(), mapping->peerId() };
+ const ChannelNameAndType nameAndType = { mapping->channelName(),
+ mapping->type(),
+ mapping->componentCount(),
+ mapping->peerId()
+ };
const int index = channelNamesAndTypes.indexOf(nameAndType);
if (index != -1) {
// Do we have any animation data for this channel? If not, don't bother
@@ -645,7 +641,10 @@ QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler,
case ChannelMapping::ChannelMappingType:
case ChannelMapping::CallbackMappingType: {
// Get the name and type
- const ChannelNameAndType nameAndType{ mapping->channelName(), mapping->type(), mappingId };
+ const ChannelNameAndType nameAndType{ mapping->channelName(),
+ mapping->type(),
+ mapping->componentCount(),
+ mappingId };
// Add if not already contained
if (!namesAndTypes.contains(nameAndType))
@@ -694,7 +693,7 @@ QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNam
int baseIndex = 0;
for (const auto &entry : namesAndTypes) {
// Populate indices in order
- const int componentCount = componentsForType(entry.type);
+ const int componentCount = entry.componentCount;
ComponentIndices indices(componentCount);
std::iota(indices.begin(), indices.end(), baseIndex);
@@ -780,6 +779,7 @@ ClipFormat generateClipFormatIndices(const QVector<ChannelNameAndType> &targetCh
const int baseIndex = clip->channelComponentBaseIndex(clipChannelIndex);
const auto channelIndices = channelComponentsToIndices(clip->channels()[clipChannelIndex],
targetChannel.type,
+ targetChannel.componentCount,
baseIndex);
std::copy(channelIndices.begin(), channelIndices.end(), formatIt);
@@ -909,7 +909,7 @@ QVector<float> defaultValueForChannel(Handler *handler,
}
// Everything else gets all zeros
- const int componentCount = componentsForType(channelDescription.type);
+ const int componentCount = mapping->componentCount();
result = QVector<float>(componentCount, 0.0f);
break;
}
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index 42402c5ec..bded12bd2 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -134,7 +134,7 @@ struct ChannelNameAndType
int jointIndex;
Qt3DCore::QNodeId mappingId;
JointTransformComponent jointTransformComponent;
- float pad; // Unused
+ int componentCount;
static const int invalidIndex = -1;
@@ -145,11 +145,12 @@ struct ChannelNameAndType
, jointIndex(-1)
, mappingId()
, jointTransformComponent(NoTransformComponent)
- , pad(0)
+ , componentCount(-1)
{}
ChannelNameAndType(const QString &_name,
int _type,
+ int componentCount,
Qt3DCore::QNodeId _mappingId = Qt3DCore::QNodeId(),
int _jointIndex = invalidIndex)
: jointName()
@@ -158,7 +159,7 @@ struct ChannelNameAndType
, jointIndex(_jointIndex)
, mappingId(_mappingId)
, jointTransformComponent(NoTransformComponent)
- , pad(0)
+ , componentCount(componentCount)
{}
ChannelNameAndType(const QString &_name,
@@ -170,8 +171,20 @@ struct ChannelNameAndType
, jointIndex(invalidIndex)
, mappingId()
, jointTransformComponent(_jointTransformComponent)
- , pad(0)
- {}
+ , componentCount(-1)
+ {
+ switch (_jointTransformComponent) {
+ case NoTransformComponent:
+ break;
+ case Scale:
+ case Translation:
+ componentCount = 3;
+ break;
+ case Rotation:
+ componentCount = 4;
+ break;
+ };
+ }
bool operator==(const ChannelNameAndType &rhs) const
{
@@ -179,7 +192,8 @@ struct ChannelNameAndType
&& type == rhs.type
&& jointIndex == rhs.jointIndex
&& mappingId == rhs.mappingId
- && jointTransformComponent == rhs.jointTransformComponent;
+ && jointTransformComponent == rhs.jointTransformComponent
+ && componentCount == rhs.componentCount;
}
};
@@ -192,7 +206,8 @@ inline QDebug operator<<(QDebug dbg, const ChannelNameAndType &nameAndType)
<< "mappingId =" << nameAndType.mappingId
<< "jointIndex =" << nameAndType.jointIndex
<< "jointName =" << nameAndType.jointName
- << "jointTransformComponent =" << nameAndType.jointTransformComponent;
+ << "jointTransformComponent =" << nameAndType.jointTransformComponent
+ << "componentCount =" << nameAndType.componentCount;
return dbg;
}
#endif
@@ -289,20 +304,18 @@ inline bool isValidNormalizedTime(float t)
}
Q_AUTOTEST_EXPORT
-int componentsForType(int type);
-
-Q_AUTOTEST_EXPORT
ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
const AnimatorEvaluationData &animatorData);
Q_AUTOTEST_EXPORT
ComponentIndices channelComponentsToIndices(const Channel &channel,
int dataType,
- int offset = 0);
+ int expectedComponentCount,
+ int offset);
Q_AUTOTEST_EXPORT
ComponentIndices channelComponentsToIndicesHelper(const Channel &channelGroup,
- int dataType,
+ int expectedComponentCount,
int offset,
const QVector<char> &suffixes);
diff --git a/src/animation/backend/channelmapping.cpp b/src/animation/backend/channelmapping.cpp
index ecae8bbae..d8572a074 100644
--- a/src/animation/backend/channelmapping.cpp
+++ b/src/animation/backend/channelmapping.cpp
@@ -55,6 +55,7 @@ ChannelMapping::ChannelMapping()
, m_targetId()
, m_property()
, m_type(static_cast<int>(QVariant::Invalid))
+ , m_componentCount(0)
, m_propertyName(nullptr)
, m_callback(nullptr)
, m_callbackFlags(0)
@@ -74,6 +75,7 @@ void ChannelMapping::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePt
m_targetId = data.targetId;
m_property = data.property;
m_type = data.type;
+ m_componentCount = data.componentCount;
m_propertyName = data.propertyName;
m_mappingType = ChannelMappingType;
break;
@@ -108,6 +110,7 @@ void ChannelMapping::cleanup()
m_property.clear();
m_type = static_cast<int>(QVariant::Invalid);
m_propertyName = nullptr;
+ m_componentCount = 0;
m_callback = nullptr;
m_callbackFlags = 0;
m_skeletonId = Qt3DCore::QNodeId();
@@ -128,6 +131,8 @@ void ChannelMapping::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
m_type = change->value().toInt();
else if (change->propertyName() == QByteArrayLiteral("propertyName"))
m_propertyName = static_cast<const char *>(const_cast<const void *>(change->value().value<void *>()));
+ else if (change->propertyName() == QByteArrayLiteral("componentCount"))
+ m_componentCount = change->value().toInt();
else if (change->propertyName() == QByteArrayLiteral("callback"))
m_callback = static_cast<QAnimationCallback *>(change->value().value<void *>());
else if (change->propertyName() == QByteArrayLiteral("callbackFlags"))
diff --git a/src/animation/backend/channelmapping_p.h b/src/animation/backend/channelmapping_p.h
index 82de5c7b4..5159adae2 100644
--- a/src/animation/backend/channelmapping_p.h
+++ b/src/animation/backend/channelmapping_p.h
@@ -93,6 +93,9 @@ public:
void setPropertyName(const char *propertyName) { m_propertyName = propertyName; }
const char *propertyName() const { return m_propertyName; }
+ void setComponentCount(int componentCount) { m_componentCount = componentCount; }
+ int componentCount() const { return m_componentCount; }
+
void setCallback(QAnimationCallback *callback) { m_callback = callback; }
QAnimationCallback *callback() const { return m_callback; }
@@ -114,6 +117,7 @@ private:
Qt3DCore::QNodeId m_targetId;
QString m_property;
int m_type;
+ int m_componentCount;
const char *m_propertyName;
// TODO: Properties from QCallbackMapping
diff --git a/src/animation/frontend/qabstractchannelmapping_p.h b/src/animation/frontend/qabstractchannelmapping_p.h
index 1a1de4ba9..2d77ee265 100644
--- a/src/animation/frontend/qabstractchannelmapping_p.h
+++ b/src/animation/frontend/qabstractchannelmapping_p.h
@@ -60,7 +60,7 @@ namespace Qt3DAnimation {
class QAbstractChannelMapping;
-class QAbstractChannelMappingPrivate : public Qt3DCore::QNodePrivate
+class Q_AUTOTEST_EXPORT QAbstractChannelMappingPrivate : public Qt3DCore::QNodePrivate
{
public:
QAbstractChannelMappingPrivate();
diff --git a/src/animation/frontend/qchannelmapping.cpp b/src/animation/frontend/qchannelmapping.cpp
index 0d9150c50..0dbe68c8c 100644
--- a/src/animation/frontend/qchannelmapping.cpp
+++ b/src/animation/frontend/qchannelmapping.cpp
@@ -47,6 +47,61 @@ QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
+namespace {
+
+template<typename T>
+int componentCountForValue(const T &)
+{
+ return 0;
+}
+
+template<>
+int componentCountForValue<QVector<float>>(const QVector<float> &v)
+{
+ return v.size();
+}
+
+template<>
+int componentCountForValue<QVariantList>(const QVariantList &v)
+{
+ return v.size();
+}
+
+
+int componentCountForType(int type, const QVariant &value)
+{
+ const int vectorOfFloatTypeId = qMetaTypeId<QVector<float>>();
+
+ if (type == vectorOfFloatTypeId)
+ return componentCountForValue<QVector<float>>(value.value<QVector<float>>());
+
+ switch (type) {
+ case QMetaType::Float:
+ case QVariant::Double:
+ return 1;
+
+ case QVariant::Vector2D:
+ return 2;
+
+ case QVariant::Vector3D:
+ case QVariant::Color:
+ return 3;
+
+ case QVariant::Vector4D:
+ case QVariant::Quaternion:
+ return 4;
+
+ case QVariant::List:
+ return componentCountForValue<QVariantList>(value.toList());
+
+ default:
+ qWarning() << "Unhandled animation type";
+ return 0;
+ }
+}
+
+} // anonymous
+
QChannelMappingPrivate::QChannelMappingPrivate()
: QAbstractChannelMappingPrivate()
, m_channelName()
@@ -54,6 +109,7 @@ QChannelMappingPrivate::QChannelMappingPrivate()
, m_property()
, m_propertyName(nullptr)
, m_type(static_cast<int>(QVariant::Invalid))
+ , m_componentCount(0)
{
m_mappingType = QChannelMappingCreatedChangeBase::ChannelMapping;
}
@@ -63,9 +119,10 @@ QChannelMappingPrivate::QChannelMappingPrivate()
Find the type of the property specified on the target node
*/
-void QChannelMappingPrivate::updatePropertyNameAndType()
+void QChannelMappingPrivate::updatePropertyNameTypeAndComponentCount()
{
int type;
+ int componentCount = 0;
const char *propertyName = nullptr;
if (!m_target || m_property.isNull()) {
@@ -76,8 +133,8 @@ void QChannelMappingPrivate::updatePropertyNameAndType()
QMetaProperty mp = mo->property(propertyIndex);
propertyName = mp.name();
type = mp.userType();
+ const QVariant currentValue = m_target->property(mp.name());
if (type == QMetaType::QVariant) {
- QVariant currentValue = m_target->property(mp.name());
if (currentValue.isValid()) {
type = currentValue.userType();
} else {
@@ -85,6 +142,7 @@ void QChannelMappingPrivate::updatePropertyNameAndType()
"Set a value first in order to be able to determine the type.");
}
}
+ componentCount = componentCountForType(type, currentValue);
}
if (m_type != type) {
@@ -98,6 +156,17 @@ void QChannelMappingPrivate::updatePropertyNameAndType()
notifyObservers(e);
}
+ if (m_componentCount != componentCount) {
+ m_componentCount = componentCount;
+
+ // Send update to the backend
+ Q_Q(QChannelMapping);
+ auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(q->id());
+ e->setPropertyName("componentCount");
+ e->setValue(QVariant(m_componentCount));
+ notifyObservers(e);
+ }
+
if (qstrcmp(m_propertyName, propertyName) != 0) {
m_propertyName = propertyName;
@@ -178,7 +247,7 @@ void QChannelMapping::setTarget(Qt3DCore::QNode *target)
d->registerDestructionHelper(d->m_target, &QChannelMapping::setTarget, d->m_target);
emit targetChanged(target);
- d->updatePropertyNameAndType();
+ d->updatePropertyNameTypeAndComponentCount();
}
void QChannelMapping::setProperty(const QString &property)
@@ -189,7 +258,7 @@ void QChannelMapping::setProperty(const QString &property)
d->m_property = property;
emit propertyChanged(property);
- d->updatePropertyNameAndType();
+ d->updatePropertyNameTypeAndComponentCount();
}
Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() const
@@ -201,6 +270,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange()
data.targetId = Qt3DCore::qIdForNode(d->m_target);
data.property = d->m_property;
data.type = d->m_type;
+ data.componentCount = d->m_componentCount;
data.propertyName = d->m_propertyName;
return creationChange;
}
diff --git a/src/animation/frontend/qchannelmapping_p.h b/src/animation/frontend/qchannelmapping_p.h
index b7de3b821..6a3f1afc5 100644
--- a/src/animation/frontend/qchannelmapping_p.h
+++ b/src/animation/frontend/qchannelmapping_p.h
@@ -50,25 +50,27 @@
#include <Qt3DAnimation/private/qabstractchannelmapping_p.h>
#include <Qt3DAnimation/qanimationcallback.h>
+#include <QVector>
QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
-class QChannelMappingPrivate : public QAbstractChannelMappingPrivate
+class Q_AUTOTEST_EXPORT QChannelMappingPrivate : public QAbstractChannelMappingPrivate
{
public:
QChannelMappingPrivate();
Q_DECLARE_PUBLIC(QChannelMapping)
- void updatePropertyNameAndType();
+ void updatePropertyNameTypeAndComponentCount();
QString m_channelName;
Qt3DCore::QNode *m_target;
QString m_property;
const char *m_propertyName;
int m_type;
+ int m_componentCount;
};
struct QChannelMappingData
@@ -77,6 +79,7 @@ struct QChannelMappingData
Qt3DCore::QNodeId targetId;
QString property;
int type;
+ int componentCount;
const char *propertyName;
};
@@ -85,4 +88,7 @@ struct QChannelMappingData
QT_END_NAMESPACE
+// Used to define the meta type id
+Q_DECLARE_METATYPE(QVector<float>) // LCOV_EXCL_LINE
+
#endif // QT3DANIMATION_QCHANNELMAPPING_P_H
diff --git a/src/core/changes/qpropertynodeaddedchange.cpp b/src/core/changes/qpropertynodeaddedchange.cpp
index 865de2731..347d7f188 100644
--- a/src/core/changes/qpropertynodeaddedchange.cpp
+++ b/src/core/changes/qpropertynodeaddedchange.cpp
@@ -85,7 +85,7 @@ QPropertyNodeAddedChange::QPropertyNodeAddedChange(QNodeId subjectId, QNode *nod
// 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.
- QNodePrivate::get(node)->_q_postConstructorInit();
+ QNodePrivate::get(node)->_q_ensureBackendNodeCreated();
}
/*! \internal */
diff --git a/src/core/configure.json b/src/core/configure.json
index e72c5ed2b..2ba205530 100644
--- a/src/core/configure.json
+++ b/src/core/configure.json
@@ -1,5 +1,9 @@
{
"module": "3dcore",
+ "depends": [
+ "gui"
+ ],
+ "condition": "module.gui && features.opengl && !config.wasm",
"testDir": "../../config.tests",
"commandline": {
diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp
index 69d774dd1..164e0feff 100644
--- a/src/core/nodes/qentity.cpp
+++ b/src/core/nodes/qentity.cpp
@@ -239,6 +239,10 @@ QNodeId QEntityPrivate::parentEntityId() const
QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const
{
+ // connect to the parentChanged signal here rather than constructor because
+ // until now there's no backend node to notify when parent changes
+ connect(this, &QNode::parentChanged, this, &QEntity::onParentChanged);
+
auto creationChange = QNodeCreatedChangePtr<QEntityData>::create(this);
auto &data = creationChange->data;
@@ -268,6 +272,17 @@ QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const
return creationChange;
}
+void QEntity::onParentChanged(QObject *)
+{
+ const auto parentID = parentEntity() ? parentEntity()->id() : Qt3DCore::QNodeId();
+ auto parentChange = Qt3DCore::QPropertyUpdatedChangePtr::create(id());
+ parentChange->setPropertyName("parentEntityUpdated");
+ parentChange->setValue(QVariant::fromValue(parentID));
+ const bool blocked = blockNotifications(false);
+ notifyObservers(parentChange);
+ blockNotifications(blocked);
+}
+
} // namespace Qt3DCore
QT_END_NAMESPACE
diff --git a/src/core/nodes/qentity.h b/src/core/nodes/qentity.h
index bbf38efb4..f1a369c48 100644
--- a/src/core/nodes/qentity.h
+++ b/src/core/nodes/qentity.h
@@ -83,6 +83,9 @@ public:
protected:
explicit QEntity(QEntityPrivate &dd, QNode *parent = nullptr);
+private Q_SLOTS:
+ void onParentChanged(QObject *);
+
private:
Q_DECLARE_PRIVATE(QEntity)
diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp
index c2373b805..900c3f8ce 100644
--- a/src/core/nodes/qnode.cpp
+++ b/src/core/nodes/qnode.cpp
@@ -101,7 +101,7 @@ void QNodePrivate::init(QNode *parent)
Q_Q(QNode);
if (m_scene) {
// schedule the backend notification and scene registering -> set observers through scene
- QMetaObject::invokeMethod(q, "_q_postConstructorInit", Qt::QueuedConnection);
+ m_scene->postConstructorInit()->addNode(q);
}
}
@@ -165,7 +165,7 @@ void QNodePrivate::notifyDestructionChangesAndRemoveFromScene()
*
* Sends a QNodeCreatedChange event to the aspects and then also notifies the
* parent backend node of its new child. This is called in a deferred manner
- * by the QNodePrivate::init() method to notify the backend of newly created
+ * by NodePostConstructorInit::processNodes to notify the backend of newly created
* nodes with a parent that is already part of the scene.
*
* Also notify the scene of this node, so it may set it's change arbiter.
@@ -873,6 +873,12 @@ void QNode::setParent(QNode *parent)
if (parentNode() == parent &&
((parent != nullptr && d->m_parentId == parentNode()->id()) || parent == nullptr))
return;
+
+ // remove ourself from postConstructorInit queue. The call to _q_setParentHelper
+ // will take care of creating the backend node if necessary depending on new parent.
+ if (d->m_scene)
+ d->m_scene->postConstructorInit()->removeNode(this);
+
d->_q_setParentHelper(parent);
// Block notifications as we want to let the _q_setParentHelper
@@ -1132,6 +1138,75 @@ const QMetaObject *QNodePrivate::findStaticMetaObject(const QMetaObject *metaObj
return lastStaticMetaobject;
}
+/*!
+ * \internal
+ *
+ * NodePostConstructorInit handles calling QNode::_q_postConstructorInit for
+ * all nodes. By keeping track of nodes that need initialization we can
+ * create them all together ensuring they get sent to the backend in a single
+ * batch.
+ */
+NodePostConstructorInit::NodePostConstructorInit(QObject *parent)
+ : QObject(parent)
+ , m_requestedProcessing(false)
+{
+}
+
+NodePostConstructorInit::~NodePostConstructorInit() {}
+
+/*!
+ * \internal
+ *
+ * Add a node to the list of nodes needing a call to _q_postConstructorInit
+ * We only add the node if it does not have an ancestor already in the queue
+ * because initializing the ancestor will initialize all it's children.
+ * This ensures that all backend nodes are created from the top-down, with
+ * all parents created before their children
+ *
+ */
+void NodePostConstructorInit::addNode(QNode *node)
+{
+ Q_ASSERT(node);
+ QNode *nextNode = node;
+ while (nextNode != nullptr && !m_nodesToConstruct.contains(QNodePrivate::get(nextNode)))
+ nextNode = nextNode->parentNode();
+
+ if (!nextNode) {
+ m_nodesToConstruct.append(QNodePrivate::get(node));
+ if (!m_requestedProcessing){
+ QMetaObject::invokeMethod(this, "processNodes", Qt::QueuedConnection);
+ m_requestedProcessing = true;
+ }
+ }
+}
+
+/*!
+ * \internal
+ *
+ * Remove a node from the queue. This will ensure none of its
+ * children get initialized
+ */
+void NodePostConstructorInit::removeNode(QNode *node)
+{
+ Q_ASSERT(node);
+ m_nodesToConstruct.removeAll(QNodePrivate::get(node));
+}
+
+/*!
+ * \internal
+ *
+ * call _q_postConstructorInit for all nodes in the queue
+ * and clear the queue
+ */
+void NodePostConstructorInit::processNodes()
+{
+ m_requestedProcessing = false;
+ while (!m_nodesToConstruct.empty()) {
+ auto node = m_nodesToConstruct.takeFirst();
+ node->_q_postConstructorInit();
+ }
+}
+
} // namespace Qt3DCore
QT_END_NAMESPACE
diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h
index fd3265870..506708762 100644
--- a/src/core/nodes/qnode_p.h
+++ b/src/core/nodes/qnode_p.h
@@ -60,6 +60,7 @@
#include <Qt3DCore/private/qobservableinterface_p.h>
#include <Qt3DCore/private/qt3dcore_global_p.h>
#include <QtCore/private/qobject_p.h>
+#include <QQueue>
QT_BEGIN_NAMESPACE
@@ -174,6 +175,23 @@ private:
QHash<QNode *, QMetaObject::Connection> m_destructionConnections;
};
+class NodePostConstructorInit : public QObject
+{
+ Q_OBJECT
+public:
+ NodePostConstructorInit(QObject *parent = nullptr);
+ virtual ~NodePostConstructorInit();
+ void removeNode(QNode *node);
+ void addNode(QNode *node);
+
+private Q_SLOTS:
+ void processNodes();
+
+private:
+ QQueue<QNodePrivate *> m_nodesToConstruct;
+ bool m_requestedProcessing;
+};
+
} // namespace Qt3DCore
QT_END_NAMESPACE
diff --git a/src/core/qscene.cpp b/src/core/qscene.cpp
index b5895c7aa..c94272e90 100644
--- a/src/core/qscene.cpp
+++ b/src/core/qscene.cpp
@@ -57,6 +57,7 @@ public:
QScenePrivate(QAspectEngine *engine)
: m_engine(engine)
, m_arbiter(nullptr)
+ , m_postConstructorInit(new NodePostConstructorInit)
, m_rootNode(nullptr)
{
}
@@ -68,6 +69,7 @@ public:
QHash<QObservableInterface *, QNodeId> m_observableToUuid;
QHash<QNodeId, QScene::NodePropertyTrackData> m_nodePropertyTrackModeLookupTable;
QLockableObserverInterface *m_arbiter;
+ QScopedPointer<NodePostConstructorInit> m_postConstructorInit;
mutable QReadWriteLock m_lock;
mutable QReadWriteLock m_nodePropertyTrackModeLock;
QNode *m_rootNode;
@@ -247,6 +249,12 @@ void QScene::removePropertyTrackDataForNode(QNodeId nodeId)
d->m_nodePropertyTrackModeLookupTable.remove(nodeId);
}
+NodePostConstructorInit *QScene::postConstructorInit() const
+{
+ Q_D(const QScene);
+ return d->m_postConstructorInit.get();
+}
+
void QScene::setRootNode(QNode *root)
{
Q_D(QScene);
diff --git a/src/core/qscene_p.h b/src/core/qscene_p.h
index 379316247..afcfb9b40 100644
--- a/src/core/qscene_p.h
+++ b/src/core/qscene_p.h
@@ -64,6 +64,7 @@ namespace Qt3DCore {
class QScenePrivate;
class QAspectEngine;
+class NodePostConstructorInit;
typedef QList<QObservableInterface *> QObservableList;
@@ -106,6 +107,8 @@ public:
void setPropertyTrackDataForNode(QNodeId id, const NodePropertyTrackData &data);
void removePropertyTrackDataForNode(QNodeId id);
+ NodePostConstructorInit* postConstructorInit() const;
+
private:
Q_DECLARE_PRIVATE(QScene)
QScopedPointer<QScenePrivate> d_ptr;
diff --git a/src/quick3d/quick3d/items/quick3dentityloader.cpp b/src/quick3d/quick3d/items/quick3dentityloader.cpp
index bdf310d02..d17492b68 100644
--- a/src/quick3d/quick3d/items/quick3dentityloader.cpp
+++ b/src/quick3d/quick3d/items/quick3dentityloader.cpp
@@ -309,9 +309,6 @@ void Quick3DEntityLoaderPrivate::_q_componentStatusChanged(QQmlComponent::Status
Q_ASSERT(m_context == nullptr);
Q_ASSERT(m_incubator == nullptr);
-
- qDebug() << Q_FUNC_INFO << status;
-
if (!m_component) {
clear();
emit q->entityChanged();
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index f9c8e29eb..8aa994eb2 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -114,7 +114,8 @@ public:
JointDirty = 1 << 11,
LayersDirty = 1 << 12,
TechniquesDirty = 1 << 13,
- LightsDirty = 1 << 14,
+ EntityHierarchyDirty= 1 << 14,
+ LightsDirty = 1 << 15,
AllDirty = 0xffffff
};
Q_DECLARE_FLAGS(BackendNodeDirtySet, BackendNodeDirtyFlag)
diff --git a/src/render/backend/entity.cpp b/src/render/backend/entity.cpp
index 8d03cd75f..d8d04aef1 100644
--- a/src/render/backend/entity.cpp
+++ b/src/render/backend/entity.cpp
@@ -65,8 +65,6 @@
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qtransform.h>
#include <Qt3DCore/private/qentity_p.h>
-#include <Qt3DCore/qpropertynoderemovedchange.h>
-#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DCore/qnodecreatedchange.h>
#include <QMatrix4x4>
@@ -95,14 +93,13 @@ Entity::~Entity()
void Entity::cleanup()
{
if (m_nodeManagers != nullptr) {
- Entity *parentEntity = parent();
- if (parentEntity != nullptr)
- parentEntity->removeChildHandle(m_handle);
m_nodeManagers->worldMatrixManager()->releaseResource(peerId());
-
qCDebug(Render::RenderNodes) << Q_FUNC_INFO;
-
}
+ if (!m_parentEntityId.isNull())
+ markDirty(AbstractRenderer::EntityHierarchyDirty);
+
+ m_parentEntityId = Qt3DCore::QNodeId();
m_worldTransform = HMatrix();
// Release all component will have to perform their own release when they receive the
// NodeDeleted notification
@@ -132,12 +129,8 @@ void Entity::cleanup()
void Entity::setParentHandle(HEntity parentHandle)
{
Q_ASSERT(m_nodeManagers);
- // Remove ourselves from previous parent children list
- Entity *parent = m_nodeManagers->renderNodesManager()->data(parentHandle);
- if (parent != nullptr && parent->m_childrenHandles.contains(m_handle))
- parent->m_childrenHandles.removeAll(m_handle);
m_parentHandle = parentHandle;
- parent = m_nodeManagers->renderNodesManager()->data(parentHandle);
+ auto parent = m_nodeManagers->renderNodesManager()->data(parentHandle);
if (parent != nullptr && !parent->m_childrenHandles.contains(m_handle))
parent->m_childrenHandles.append(m_handle);
}
@@ -159,8 +152,8 @@ void Entity::initializeFromPeer(const QNodeCreatedChangeBasePtr &change)
// Note this is *not* the parentId as that is the ID of the parent QNode, which is not
// necessarily the same as the parent QEntity (which may be further up the tree).
- const QNodeId parentEntityId = data.parentEntityId;
- qCDebug(Render::RenderNodes) << "Creating Entity id =" << peerId() << "parentId =" << parentEntityId;
+ m_parentEntityId = data.parentEntityId;
+ qCDebug(Render::RenderNodes) << "Creating Entity id =" << peerId() << "parentId =" << m_parentEntityId;
// TODO: Store string id instead and only in debug mode
//m_objectName = peer->objectName();
@@ -187,10 +180,7 @@ void Entity::initializeFromPeer(const QNodeCreatedChangeBasePtr &change)
for (const auto &idAndType : qAsConst(data.componentIdsAndTypes))
addComponent(idAndType);
- if (!parentEntityId.isNull())
- setParentHandle(m_nodeManagers->renderNodesManager()->lookupHandle(parentEntityId));
- else
- qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "No parent entity found for Entity" << peerId();
+ markDirty(AbstractRenderer::EntityHierarchyDirty);
}
void Entity::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
@@ -214,30 +204,21 @@ void Entity::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
break;
}
- case PropertyValueAdded: {
- QPropertyNodeAddedChangePtr change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QEntity::staticMetaObject)) {
- appendChildHandle(m_nodeManagers->renderNodesManager()->lookupHandle(change->addedNodeId()));
- markDirty(AbstractRenderer::AllDirty);
- }
- break;
- }
-
- case PropertyValueRemoved: {
- QPropertyNodeRemovedChangePtr change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QEntity::staticMetaObject)) {
- removeChildHandle(m_nodeManagers->renderNodesManager()->lookupHandle(change->removedNodeId()));
- markDirty(AbstractRenderer::AllDirty);
- }
- break;
- }
-
case PropertyUpdated: {
QPropertyUpdatedChangePtr change = qSharedPointerCast<QPropertyUpdatedChange>(e);
if (change->propertyName() == QByteArrayLiteral("enabled")) {
// We only mark as dirty the renderer
markDirty(AbstractRenderer::EntityEnabledDirty);
// We let QBackendNode::sceneChangeEvent change the enabled property
+ } else if (change->propertyName() == QByteArrayLiteral("parentEntityUpdated")) {
+ auto newParent = change->value().value<Qt3DCore::QNodeId>();
+ qCDebug(Render::RenderNodes) << "Setting parent for " << peerId() << ", new parentId =" << newParent;
+ if (m_parentEntityId != newParent) {
+ m_parentEntityId = newParent;
+ // TODO: change to EventHierarchyDirty and update renderer to
+ // ensure all jobs are run that depend on Entity hierarchy.
+ markDirty(AbstractRenderer::AllDirty);
+ }
}
break;
@@ -265,6 +246,27 @@ Entity *Entity::parent() const
return m_nodeManagers->renderNodesManager()->data(m_parentHandle);
}
+
+// clearEntityHierarchy and rebuildEntityHierarchy should only be called
+// from UpdateEntityHierarchyJob to update the entity hierarchy for the
+// entire scene at once
+void Entity::clearEntityHierarchy()
+{
+ m_childrenHandles.clear();
+ m_parentHandle = HEntity();
+}
+
+// clearEntityHierarchy and rebuildEntityHierarchy should only be called
+// from UpdateEntityHierarchyJob to update the entity hierarchy for the
+// entire scene at once
+void Entity::rebuildEntityHierarchy()
+{
+ if (!m_parentEntityId.isNull())
+ setParentHandle(m_nodeManagers->renderNodesManager()->lookupHandle(m_parentEntityId));
+ else
+ qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "No parent entity found for Entity" << peerId();
+}
+
void Entity::appendChildHandle(HEntity childHandle)
{
if (!m_childrenHandles.contains(childHandle)) {
diff --git a/src/render/backend/entity_p.h b/src/render/backend/entity_p.h
index 149ac1143..c170fd42d 100644
--- a/src/render/backend/entity_p.h
+++ b/src/render/backend/entity_p.h
@@ -97,6 +97,10 @@ public:
HEntity handle() const { return m_handle; }
Entity *parent() const;
HEntity parentHandle() const { return m_parentHandle; }
+ Qt3DCore::QNodeId parentEntityId() const { return m_parentEntityId; }
+
+ void clearEntityHierarchy();
+ void rebuildEntityHierarchy();
void appendChildHandle(HEntity childHandle);
void removeChildHandle(HEntity childHandle) { m_childrenHandles.removeOne(childHandle); }
@@ -181,6 +185,8 @@ private:
HEntity m_parentHandle;
QVector<HEntity > m_childrenHandles;
+ Qt3DCore::QNodeId m_parentEntityId;
+
HMatrix m_worldTransform;
QSharedPointer<Sphere> m_localBoundingVolume;
QSharedPointer<Sphere> m_worldBoundingVolume;
diff --git a/src/render/backend/managers.cpp b/src/render/backend/managers.cpp
index 3b1f8e910..26fd68600 100644
--- a/src/render/backend/managers.cpp
+++ b/src/render/backend/managers.cpp
@@ -72,7 +72,11 @@ FrameGraphNode* FrameGraphManager::lookupNode(Qt3DCore::QNodeId id) const
void FrameGraphManager::releaseNode(Qt3DCore::QNodeId id)
{
- delete m_nodes.take(id);
+ auto node = m_nodes.take(id);
+ if (node) {
+ node->cleanup();
+ delete node;
+ }
}
void SkeletonManager::addDirtySkeleton(DirtyFlag dirtyFlag, HSkeleton skeletonHandle)
diff --git a/src/render/configure.json b/src/render/configure.json
index ee5002707..02a6e3747 100644
--- a/src/render/configure.json
+++ b/src/render/configure.json
@@ -1,5 +1,9 @@
{
"module": "3drender",
+ "depends": [
+ "3dcore"
+ ],
+ "condition": "module.3dcore",
"testDir": "./config.tests",
"commandline": {
diff --git a/src/render/framegraph/framegraphnode.cpp b/src/render/framegraph/framegraphnode.cpp
index 562548e46..41a94f0e3 100644
--- a/src/render/framegraph/framegraphnode.cpp
+++ b/src/render/framegraph/framegraphnode.cpp
@@ -40,9 +40,8 @@
#include "framegraphnode_p.h"
#include <Qt3DRender/private/renderer_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
-#include <Qt3DCore/qpropertynoderemovedchange.h>
-#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DRender/qframegraphnodecreatedchange.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
QT_BEGIN_NAMESPACE
@@ -101,28 +100,6 @@ void FrameGraphNode::setParentId(Qt3DCore::QNodeId parentId)
}
}
-void FrameGraphNode::appendChildId(Qt3DCore::QNodeId childId)
-{
- if (!m_childrenIds.contains(childId)) {
- FrameGraphNode *child = m_manager->lookupNode(childId);
- if (child != nullptr) {
- m_childrenIds.append(childId);
- child->m_parentId = peerId();
- }
- }
-}
-
-void FrameGraphNode::removeChildId(Qt3DCore::QNodeId childId)
-{
- if (m_childrenIds.contains(childId)) {
- FrameGraphNode *child = m_manager->lookupNode(childId);
- if (child != nullptr) {
- child->m_parentId = Qt3DCore::QNodeId();
- }
- m_childrenIds.removeAll(childId);
- }
-}
-
Qt3DCore::QNodeId FrameGraphNode::parentId() const
{
return m_parentId;
@@ -155,28 +132,15 @@ void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
switch (e->type()) {
- case Qt3DCore::PropertyValueAdded: {
- Qt3DCore::QPropertyNodeAddedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
- appendChildId(change->addedNodeId());
- markDirty(AbstractRenderer::FrameGraphDirty);
- }
- break;
- }
-
- case Qt3DCore::PropertyValueRemoved: {
- Qt3DCore::QPropertyNodeRemovedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
- removeChildId(change->removedNodeId());
- markDirty(AbstractRenderer::FrameGraphDirty);
- }
- break;
- }
case Qt3DCore::PropertyUpdated: {
Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) {
d_func()->m_enabled = propertyChange->value().toBool();
markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("parentFrameGraphUpdated")) {
+ auto newParent = propertyChange->value().value<Qt3DCore::QNodeId>();
+ setParentId(newParent);
+ markDirty(AbstractRenderer::AllDirty);
}
break;
}
@@ -185,6 +149,12 @@ void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
}
}
+void FrameGraphNode::cleanup()
+{
+ setParentId({});
+}
+
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/src/render/framegraph/framegraphnode_p.h b/src/render/framegraph/framegraphnode_p.h
index 5bd9b1b12..420abce56 100644
--- a/src/render/framegraph/framegraphnode_p.h
+++ b/src/render/framegraph/framegraphnode_p.h
@@ -112,8 +112,6 @@ public:
FrameGraphManager *manager() const;
void setParentId(Qt3DCore::QNodeId parentId);
- void appendChildId(Qt3DCore::QNodeId childHandle);
- void removeChildId(Qt3DCore::QNodeId childHandle);
Qt3DCore::QNodeId parentId() const;
QVector<Qt3DCore::QNodeId> childrenIds() const;
@@ -121,6 +119,8 @@ public:
FrameGraphNode *parent() const;
QVector<FrameGraphNode *> children() const;
+ void cleanup();
+
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
protected:
diff --git a/src/render/framegraph/qframegraphnode.cpp b/src/render/framegraph/qframegraphnode.cpp
index 9acfd1209..d52b728a8 100644
--- a/src/render/framegraph/qframegraphnode.cpp
+++ b/src/render/framegraph/qframegraphnode.cpp
@@ -40,6 +40,7 @@
#include "qframegraphnode.h"
#include "qframegraphnode_p.h"
#include <Qt3DRender/qframegraphnodecreatedchange.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/QNode>
@@ -248,9 +249,24 @@ QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent)
Qt3DCore::QNodeCreatedChangeBasePtr QFrameGraphNode::createNodeCreationChange() const
{
+ // connect to the parentChanged signal here rather than constructor because
+ // until now there's no backend node to notify when parent changes
+ connect(this, &QNode::parentChanged, this, &QFrameGraphNode::onParentChanged);
+
return QFrameGraphNodeCreatedChangeBasePtr::create(this);
}
+void QFrameGraphNode::onParentChanged(QObject *)
+{
+ const auto parentID = parentFrameGraphNode() ? parentFrameGraphNode()->id() : Qt3DCore::QNodeId();
+ auto parentChange = Qt3DCore::QPropertyUpdatedChangePtr::create(id());
+ parentChange->setPropertyName("parentFrameGraphUpdated");
+ parentChange->setValue(QVariant::fromValue(parentID));
+ const bool blocked = blockNotifications(false);
+ notifyObservers(parentChange);
+ blockNotifications(blocked);
+}
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/render/framegraph/qframegraphnode.h b/src/render/framegraph/qframegraphnode.h
index 9b08c98d3..826f01a1e 100644
--- a/src/render/framegraph/qframegraphnode.h
+++ b/src/render/framegraph/qframegraphnode.h
@@ -63,6 +63,9 @@ protected:
explicit QFrameGraphNode(QFrameGraphNodePrivate &dd, Qt3DCore::QNode *parent = nullptr);
Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+private Q_SLOTS:
+ void onParentChanged(QObject *);
+
private:
Q_DECLARE_PRIVATE(QFrameGraphNode)
};
diff --git a/src/render/jobs/job_common_p.h b/src/render/jobs/job_common_p.h
index 9c83624b8..5fe16f933 100644
--- a/src/render/jobs/job_common_p.h
+++ b/src/render/jobs/job_common_p.h
@@ -109,6 +109,7 @@ namespace JobTypes {
UpdateLayerEntity,
SendTextureChangesToFrontend,
SendSetFenceHandlesToFrontend,
+ UpdateEntityHierarchy,
};
} // JobTypes
diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri
index 2181e4a95..6cdf891fc 100644
--- a/src/render/jobs/jobs.pri
+++ b/src/render/jobs/jobs.pri
@@ -30,6 +30,7 @@ HEADERS += \
$$PWD/filterproximitydistancejob_p.h \
$$PWD/abstractpickingjob_p.h \
$$PWD/raycastingjob_p.h \
+ $$PWD/updateentityhierarchyjob_p.h \
$$PWD/updateentitylayersjob_p.h
SOURCES += \
@@ -59,5 +60,6 @@ SOURCES += \
$$PWD/filterproximitydistancejob.cpp \
$$PWD/abstractpickingjob.cpp \
$$PWD/raycastingjob.cpp \
+ $$PWD/updateentityhierarchyjob.cpp \
$$PWD/updateentitylayersjob.cpp
diff --git a/src/render/jobs/updateentityhierarchyjob.cpp b/src/render/jobs/updateentityhierarchyjob.cpp
new file mode 100644
index 000000000..7c18514bb
--- /dev/null
+++ b/src/render/jobs/updateentityhierarchyjob.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "updateentityhierarchyjob_p.h"
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+UpdateEntityHierarchyJob::UpdateEntityHierarchyJob()
+ : m_manager(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::UpdateEntityHierarchy, 0);
+}
+
+void UpdateEntityHierarchyJob::run()
+{
+ Q_ASSERT(m_manager);
+ EntityManager *entityManager = m_manager->renderNodesManager();
+
+ const QVector<HEntity> handles = entityManager->activeHandles();
+
+ // Clear the parents and children
+ for (const HEntity &handle : handles) {
+ Entity *entity = entityManager->data(handle);
+ entity->clearEntityHierarchy();
+ }
+ for (const HEntity &handle : handles) {
+ Entity *entity = entityManager->data(handle);
+ entity->rebuildEntityHierarchy();
+ }
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/jobs/updateentityhierarchyjob_p.h b/src/render/jobs/updateentityhierarchyjob_p.h
new file mode 100644
index 000000000..fd2b13631
--- /dev/null
+++ b/src/render/jobs/updateentityhierarchyjob_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H
+#define QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <Qt3DCore/qaspectjob.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Entity;
+class NodeManagers;
+
+class Q_3DRENDERSHARED_PRIVATE_EXPORT UpdateEntityHierarchyJob: public Qt3DCore::QAspectJob
+{
+public:
+ UpdateEntityHierarchyJob();
+
+ inline void setManager(NodeManagers *manager) { m_manager = manager; }
+ inline NodeManagers *manager() const { return m_manager; }
+
+ // QAspectJob interface
+ void run() final;
+
+private:
+ NodeManagers *m_manager;
+};
+
+
+using UpdateEntityHierarchyJobPtr = QSharedPointer<UpdateEntityHierarchyJob>;
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H
diff --git a/src/render/jobs/updateentitylayersjob_p.h b/src/render/jobs/updateentitylayersjob_p.h
index 13cc2fc4c..5d36042c4 100644
--- a/src/render/jobs/updateentitylayersjob_p.h
+++ b/src/render/jobs/updateentitylayersjob_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index 012ec7056..4e40906a6 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -200,6 +200,7 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_sendSetFenceHandlesToFrontendJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend))
, m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering))
, m_syncLoadingJobs(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncLoadingJobs))
+ , m_updateEntityHierarchyJob(Render::UpdateEntityHierarchyJobPtr::create())
, m_ownedContext(false)
, m_offscreenHelper(nullptr)
#if QT_CONFIG(qt3d_profile_jobs)
@@ -212,6 +213,9 @@ Renderer::Renderer(QRenderAspect::RenderType type)
if (m_renderThread)
m_renderThread->waitForStart();
+ m_worldTransformJob->addDependency(m_updateEntityHierarchyJob);
+ m_updateEntityLayersJob->addDependency(m_updateEntityHierarchyJob);
+
// Create jobs to update transforms and bounding volumes
// We can only update bounding volumes once all world transforms are known
m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
@@ -297,6 +301,7 @@ void Renderer::setNodeManagers(NodeManagers *managers)
m_updateMeshTriangleListJob->setManagers(m_nodesManager);
m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
m_updateEntityLayersJob->setManager(m_nodesManager);
+ m_updateEntityHierarchyJob->setManager(m_nodesManager);
}
void Renderer::setServices(QServiceLocator *services)
@@ -1752,14 +1757,17 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
// Add jobs
const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
- if (entitiesEnabledDirty) {
+ const bool entityHierarchyNeedsToBeRebuilt = dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty;
+ if (entitiesEnabledDirty || entityHierarchyNeedsToBeRebuilt) {
renderBinJobs.push_back(m_updateTreeEnabledJob);
// This dependency is added here because we clear all dependencies
// at the start of this function.
m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
+ m_calculateBoundingVolumeJob->addDependency(m_updateEntityHierarchyJob);
}
- if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty ||
+ dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty) {
renderBinJobs.push_back(m_worldTransformJob);
renderBinJobs.push_back(m_updateWorldBoundingVolumeJob);
renderBinJobs.push_back(m_updateShaderDataTransformJob);
@@ -1772,6 +1780,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
}
if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty ||
+ dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty ||
dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
renderBinJobs.push_back(m_expandBoundingVolumeJob);
}
@@ -1798,7 +1807,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
// Layer cache is dependent on layers, layer filters (hence FG structure
// changes) and the enabled flag on entities
const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty;
- const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
+ const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty || entityHierarchyNeedsToBeRebuilt;
const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty;
const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty;
@@ -1806,6 +1815,10 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty;
const bool materialCacheNeedsToBeRebuilt = materialDirty || frameGraphDirty;
+ // Rebuild Entity Hierarchy if dirty
+ if (entityHierarchyNeedsToBeRebuilt)
+ renderBinJobs.push_back(m_updateEntityHierarchyJob);
+
// Rebuild Entity Layers list if layers are dirty
if (layersDirty)
renderBinJobs.push_back(m_updateEntityLayersJob);
@@ -1835,6 +1848,10 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
builder.setComputableCacheNeedsToBeRebuilt(computeableDirty);
builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty);
builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt);
+ builder.setRenderableCacheNeedsToBeRebuilt(renderableDirty);
+ builder.setComputableCacheNeedsToBeRebuilt(computeableDirty);
+ builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty);
+
builder.prepareJobs();
renderBinJobs.append(builder.buildJobHierachy());
}
diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h
index 9a2c99c30..e82d18f77 100644
--- a/src/render/renderers/opengl/renderer/renderer_p.h
+++ b/src/render/renderers/opengl/renderer/renderer_p.h
@@ -78,6 +78,7 @@
#include <Qt3DRender/private/filtercompatibletechniquejob_p.h>
#include <Qt3DRender/private/updateskinningpalettejob_p.h>
#include <Qt3DRender/private/updateentitylayersjob_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/private/renderercache_p.h>
#include <Qt3DRender/private/texture_p.h>
#include <Qt3DRender/private/glfence_p.h>
@@ -367,6 +368,7 @@ private:
UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob;
FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob;
UpdateEntityLayersJobPtr m_updateEntityLayersJob;
+ UpdateEntityHierarchyJobPtr m_updateEntityHierarchyJob;
QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests;
diff --git a/src/src.pro b/src/src.pro
index 853ae8fb5..0a3ed8cb7 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -1,5 +1,8 @@
TEMPLATE = subdirs
+!qtHaveModule(gui): \
+ return()
+
src_core.subdir = $$PWD/core
src_core.target = sub-core
diff --git a/tests/auto/animation/animation.pro b/tests/auto/animation/animation.pro
index 1226f9498..c600da690 100644
--- a/tests/auto/animation/animation.pro
+++ b/tests/auto/animation/animation.pro
@@ -5,7 +5,6 @@ SUBDIRS += \
qanimationcliploader \
qclipanimator \
qblendedclipanimator \
- qchannelmapping \
qchannelmapper \
qclipblendvalue \
qanimationcontroller \
@@ -40,5 +39,6 @@ qtConfig(private_tests) {
qabstractanimation \
clock \
skeleton \
- findrunningclipanimatorsjob
+ findrunningclipanimatorsjob \
+ qchannelmapping
}
diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp
index ee393b366..7a7abb1b3 100644
--- a/tests/auto/animation/animationutils/tst_animationutils.cpp
+++ b/tests/auto/animation/animationutils/tst_animationutils.cpp
@@ -147,7 +147,8 @@ public:
const Qt3DCore::QNodeId targetId,
const QString &property,
const char *propertyName,
- int type)
+ int type,
+ int componentCount)
{
auto channelMappingId = Qt3DCore::QNodeId::createId();
ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(channelMappingId);
@@ -159,6 +160,7 @@ public:
channelMapping->setChannelName(channelName);
channelMapping->setType(type);
channelMapping->setMappingType(ChannelMapping::ChannelMappingType);
+ channelMapping->setComponentCount(componentCount);
return channelMapping;
}
@@ -292,28 +294,43 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
QVector<ChannelMapping *> channelMappings = { channelMapping };
// Create a few channels in the format description
ChannelNameAndType rotation = { QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
channelMapping->peerId() };
ChannelNameAndType location = { QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
ChannelNameAndType baseColor = { QLatin1String("BaseColor"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
ChannelNameAndType metalness = { QLatin1String("Metalness"),
static_cast<int>(QVariant::Double),
+ 1,
channelMapping->peerId() };
ChannelNameAndType roughness = { QLatin1String("Roughness"),
static_cast<int>(QVariant::Double),
+ 1,
channelMapping->peerId() };
+ ChannelNameAndType morphTargetWeightsList = { QLatin1String("MorphTargetWeightsList"),
+ static_cast<int>(QVariant::List),
+ 5,
+ channelMapping->peerId() };
+ ChannelNameAndType morphTargetWeightsVec = { QLatin1String("MorphTargetWeightsVec"),
+ qMetaTypeId<QVector<float>>(),
+ 6,
+ channelMapping->peerId() };
QVector<ChannelNameAndType> channelNamesAndTypes
- = { rotation, location, baseColor, metalness, roughness };
+ = { rotation, location, baseColor, metalness, roughness,
+ morphTargetWeightsList, morphTargetWeightsVec };
// And the matching indices
ComponentIndices rotationIndices = { 0, 1, 2, 3 };
@@ -321,15 +338,20 @@ private Q_SLOTS:
ComponentIndices baseColorIndices = { 7, 8, 9 };
ComponentIndices metalnessIndices = { 10 };
ComponentIndices roughnessIndices = { 11 };
+ ComponentIndices morphTargetListIndices = { 12, 13, 14, 15, 16 };
+ ComponentIndices morphTargetVecIndices = { 17, 18, 19, 20, 21, 22 };
QVector<ComponentIndices> channelComponentIndices
= { rotationIndices, locationIndices, baseColorIndices,
- metalnessIndices, roughnessIndices };
+ metalnessIndices, roughnessIndices, morphTargetListIndices,
+ morphTargetVecIndices };
QVector<QBitArray> sourceClipMask = { QBitArray(4, true),
QBitArray(3, true),
QBitArray(3, true),
QBitArray(1, true),
- QBitArray(1, true) };
+ QBitArray(1, true),
+ QBitArray(5, true),
+ QBitArray(6, true) };
MappingData expectedMapping;
expectedMapping.targetId = channelMapping->targetId();
@@ -356,59 +378,83 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
auto metalnessMapping = createChannelMapping(handler,
QLatin1String("Metalness"),
Qt3DCore::QNodeId::createId(),
QLatin1String("metalness"),
"metalness",
- static_cast<int>(QVariant::Double));
+ static_cast<int>(QVariant::Double),
+ 1);
auto baseColorMapping = createChannelMapping(handler,
QLatin1String("BaseColor"),
Qt3DCore::QNodeId::createId(),
QLatin1String("baseColor"),
"baseColor",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
auto roughnessMapping = createChannelMapping(handler,
QLatin1String("Roughness"),
Qt3DCore::QNodeId::createId(),
QLatin1String("roughness"),
"roughness",
- static_cast<int>(QVariant::Double));
+ static_cast<int>(QVariant::Double),
+ 1);
auto rotationMapping = createChannelMapping(handler,
QLatin1String("Rotation"),
Qt3DCore::QNodeId::createId(),
QLatin1String("rotation"),
"rotation",
- static_cast<int>(QVariant::Quaternion));
+ static_cast<int>(QVariant::Quaternion),
+ 4);
+
+ auto morphTargetMapping = createChannelMapping(handler,
+ QLatin1String("MorphTargetWeights"),
+ Qt3DCore::QNodeId::createId(),
+ QLatin1String("weights"),
+ "weights",
+ static_cast<int>(QVariant::List),
+ 5);
+
QVector<ChannelMapping *> channelMappings
= { locationMapping, metalnessMapping,
baseColorMapping, roughnessMapping,
- rotationMapping };
+ rotationMapping, morphTargetMapping };
// Create a few channels in the format description
ChannelNameAndType rotation = { QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
rotationMapping->peerId() };
ChannelNameAndType location = { QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
locationMapping->peerId() };
ChannelNameAndType baseColor = { QLatin1String("BaseColor"),
static_cast<int>(QVariant::Vector3D),
+ 3,
baseColorMapping->peerId() };
ChannelNameAndType metalness = { QLatin1String("Metalness"),
static_cast<int>(QVariant::Double),
+ 1,
metalnessMapping->peerId() };
ChannelNameAndType roughness = { QLatin1String("Roughness"),
static_cast<int>(QVariant::Double),
+ 1,
roughnessMapping->peerId() };
+ ChannelNameAndType morphTarget = { QLatin1String("MorphTargetWeights"),
+ static_cast<int>(QVariant::List),
+ 5,
+ morphTargetMapping->peerId() };
QVector<ChannelNameAndType> channelNamesAndTypes
- = { rotation, location, baseColor, metalness, roughness };
+ = { rotation, location, baseColor, metalness, roughness,
+ morphTarget };
// And the matching indices
ComponentIndices rotationIndices = { 0, 1, 2, 3 };
@@ -416,15 +462,17 @@ private Q_SLOTS:
ComponentIndices baseColorIndices = { 7, 8, 9 };
ComponentIndices metalnessIndices = { 10 };
ComponentIndices roughnessIndices = { 11 };
+ ComponentIndices morphTargetIndices = { 12, 13, 14, 15, 16 };
QVector<ComponentIndices> channelComponentIndices
= { rotationIndices, locationIndices, baseColorIndices,
- metalnessIndices, roughnessIndices };
+ metalnessIndices, roughnessIndices, morphTargetIndices };
QVector<QBitArray> sourceClipMask = { QBitArray(4, true),
QBitArray(3, true),
QBitArray(3, true),
QBitArray(1, true),
- QBitArray(1, true) };
+ QBitArray(1, true),
+ QBitArray(5, true) };
MappingData expectedLocationMapping;
expectedLocationMapping.targetId = locationMapping->targetId();
@@ -456,12 +504,19 @@ private Q_SLOTS:
expectedRotationMapping.type = rotationMapping->type();
expectedRotationMapping.channelIndices = rotationIndices;
+ MappingData expectedMorphTargetMapping;
+ expectedMorphTargetMapping.targetId = morphTargetMapping->targetId();
+ expectedMorphTargetMapping.propertyName = morphTargetMapping->propertyName();
+ expectedMorphTargetMapping.type = morphTargetMapping->type();
+ expectedMorphTargetMapping.channelIndices = morphTargetIndices;
+
QVector<MappingData> expectedResults
= { expectedLocationMapping,
expectedMetalnessMapping,
expectedBaseColorMapping,
expectedRoughnessMapping,
- expectedRotationMapping };
+ expectedRotationMapping,
+ expectedMorphTargetMapping };
QTest::newRow("multiple mappings")
<< handler
@@ -486,6 +541,7 @@ private Q_SLOTS:
for (int i = 0; i < jointCount; ++i) {
ChannelNameAndType locationDescription = { QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
locationDescription.jointIndex = i;
locationDescription.jointTransformComponent = Translation;
@@ -493,6 +549,7 @@ private Q_SLOTS:
ChannelNameAndType rotationDescription = { QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
channelMapping->peerId() };
rotationDescription.jointIndex = i;
rotationDescription.jointTransformComponent = Rotation;
@@ -500,6 +557,7 @@ private Q_SLOTS:
ChannelNameAndType scaleDescription = { QLatin1String("Scale"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
scaleDescription.jointIndex = i;
scaleDescription.jointTransformComponent = Scale;
@@ -1004,6 +1062,35 @@ private Q_SLOTS:
expectedChanges.clear();
}
+ // Single property, QVariantList
+ {
+ animatorId = Qt3DCore::QNodeId::createId();
+ MappingData mapping;
+ mapping.targetId = Qt3DCore::QNodeId::createId();
+ mapping.propertyName = "weights";
+ mapping.type = static_cast<int>(QVariant::List);
+ mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6;
+ mappingData.push_back(mapping);
+ channelResults = QVector<float>() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f;
+ finalFrame = false;
+ normalizedTime = 1.1f; // Invalid
+
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(mapping.targetId);
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
+ change->setPropertyName(mapping.propertyName);
+ QVariantList expectedValue = QVariantList() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f;
+ change->setValue(QVariant::fromValue(expectedValue));
+ expectedChanges.push_back(change);
+
+ QTest::newRow("QVariantList weights")
+ << animatorId << mappingData << channelResults << normalizedTime
+ << finalFrame << expectedChanges;
+
+ mappingData.clear();
+ channelResults.clear();
+ expectedChanges.clear();
+ }
+
}
void checkPreparePropertyChanges()
@@ -1396,12 +1483,14 @@ private Q_SLOTS:
{
QTest::addColumn<Channel>("channel");
QTest::addColumn<int>("dataType");
+ QTest::addColumn<int>("expectedChannelComponentCount");
QTest::addColumn<int>("offset");
QTest::addColumn<QVector<char>>("suffixes");
QTest::addColumn<QVector<int>>("expectedResults");
Channel channel;
int dataType;
+ int expectedChannelComponentCount;
int offset;
QVector<char> suffixes;
QVector<int> expectedResults;
@@ -1414,20 +1503,22 @@ private Q_SLOTS:
// leave 'name' empty
dataType = static_cast<int>(QVariant::Vector3D);
+ expectedChannelComponentCount = 3;
offset = 0;
// suffixes expected to be ignored
expectedResults = (QVector<int>() << 0 << 1 << 2);
QTest::newRow("vec3 location, pre-sorted, no component names, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 4;
expectedResults = (QVector<int>() << 4 << 5 << 6);
QTest::newRow("vec3 location, pre-sorted, no component names, offset = 4")
- << channel << dataType << offset << suffixes << expectedResults;
-
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
}
@@ -1441,19 +1532,22 @@ private Q_SLOTS:
channel.channelComponents[2].name = QLatin1String("Location Z");
dataType = static_cast<int>(QVariant::Vector3D);
+ expectedChannelComponentCount = 3;
offset = 0;
suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
expectedResults = (QVector<int>() << 0 << 1 << 2);
QTest::newRow("vec3 location, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 4;
expectedResults = (QVector<int>() << 4 << 5 << 6);
QTest::newRow("vec3 location, offset = 4")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1468,19 +1562,22 @@ private Q_SLOTS:
channel.channelComponents[1].name = QLatin1String("pos Y");
dataType = static_cast<int>(QVariant::Vector2D);
+ expectedChannelComponentCount = 2;
offset = 0;
suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
expectedResults = (QVector<int>() << 0 << 1);
QTest::newRow("vec2 pos, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 2;
expectedResults = (QVector<int>() << 2 << 3);
QTest::newRow("vec2 pos, offset = 2")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1497,19 +1594,22 @@ private Q_SLOTS:
channel.channelComponents[3].name = QLatin1String("foo W");
dataType = static_cast<int>(QVariant::Vector4D);
+ expectedChannelComponentCount = 4;
offset = 0;
suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
QTest::newRow("vec4 foo, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
QTest::newRow("vec4 foo, offset = 10")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1523,19 +1623,22 @@ private Q_SLOTS:
channel.channelComponents[0].name = QLatin1String("Mass X");
dataType = static_cast<int>(QVariant::Double);
+ expectedChannelComponentCount = 1;
offset = 0;
suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
expectedResults = (QVector<int>() << 0);
QTest::newRow("double Mass, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 5;
expectedResults = (QVector<int>() << 5);
QTest::newRow("double Mass, offset = 5")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1552,19 +1655,22 @@ private Q_SLOTS:
channel.channelComponents[3].name = QLatin1String("Rotation Z");
dataType = static_cast<int>(QVariant::Quaternion);
+ expectedChannelComponentCount = 4;
offset = 0;
suffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z');
expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
QTest::newRow("quaternion Rotation, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
QTest::newRow("quaternion Rotation, offset = 10")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1581,19 +1687,22 @@ private Q_SLOTS:
channel.channelComponents[3].name = QLatin1String("Rotation Y");
dataType = static_cast<int>(QVariant::Quaternion);
+ expectedChannelComponentCount = 4;
offset = 0;
suffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z');
expectedResults = (QVector<int>() << 1 << 0 << 3 << 2);
QTest::newRow("quaternion Rotation, offset = 0, randomized")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 11 << 10 << 13 << 12);
QTest::newRow("quaternion Rotation, offset = 10, randomized")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
@@ -1609,37 +1718,95 @@ private Q_SLOTS:
channel.channelComponents[2].name = QLatin1String("Color B");
dataType = static_cast<int>(QVariant::Color);
+ expectedChannelComponentCount = 3;
offset = 0;
suffixes = (QVector<char>() << 'R' << 'G' << 'B');
expectedResults = (QVector<int>() << 0 << 1 << 2);
QTest::newRow("QColor Color, offset = 0")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 10 << 11 << 12);
QTest::newRow("QColor Color, offset = 10")
- << channel << dataType << offset << suffixes << expectedResults;
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
suffixes.clear();
expectedResults.clear();
}
+
+ // weights as list with and without offset
+ {
+ channel = Channel();
+ channel.name = QLatin1String("MorphWeights");
+ channel.channelComponents.resize(6);
+ // leave channel component names empty
+
+ dataType = static_cast<int>(QVariant::List);
+ expectedChannelComponentCount = 6;
+ offset = 0;
+ // suffixes expected to be ignored
+ expectedResults = (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5);
+
+ QTest::newRow("MorphWeights List count = 6, offset = 0")
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
+ expectedResults.clear();
+
+ offset = 10;
+ expectedResults = (QVector<int>() << 10 << 11 << 12 << 13 << 14 << 15);
+ QTest::newRow("MorphWeights List count = 6, offset = 10")
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
+ suffixes.clear();
+ expectedResults.clear();
+ }
+
+ // weights as vec of float with and without offset
+ {
+ channel = Channel();
+ channel.name = QLatin1String("MorphWeights");
+ channel.channelComponents.resize(6);
+ // leave channel component names empty
+
+ dataType = qMetaTypeId<QVector<float>>();
+ expectedChannelComponentCount = 6;
+ offset = 0;
+ // suffixes expected to be ignored
+ expectedResults = (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5);
+
+ QTest::newRow("MorphWeights Vec count = 6, offset = 0")
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
+ expectedResults.clear();
+
+ offset = 10;
+ expectedResults = (QVector<int>() << 10 << 11 << 12 << 13 << 14 << 15);
+ QTest::newRow("MorphWeights Vec count = 6, offset = 10")
+ << channel << dataType << expectedChannelComponentCount
+ << offset << suffixes << expectedResults;
+ suffixes.clear();
+ expectedResults.clear();
+ }
}
void checkChannelComponentsToIndicesHelper()
{
// GIVEN
QFETCH(Channel, channel);
- QFETCH(int, dataType);
QFETCH(int, offset);
+ QFETCH(int, expectedChannelComponentCount);
QFETCH(QVector<char>, suffixes);
QFETCH(QVector<int>, expectedResults);
// WHEN
QVector<int> actualResults
- = channelComponentsToIndicesHelper(channel, dataType, offset, suffixes);
+ = channelComponentsToIndicesHelper(channel, expectedChannelComponentCount,
+ offset, suffixes);
// THEN
QCOMPARE(actualResults.size(), expectedResults.size());
@@ -1652,11 +1819,13 @@ private Q_SLOTS:
{
QTest::addColumn<Channel>("channel");
QTest::addColumn<int>("dataType");
+ QTest::addColumn<int>("componentCount");
QTest::addColumn<int>("offset");
QTest::addColumn<QVector<int>>("expectedResults");
Channel channel;
int dataType;
+ int componentCount;
int offset;
QVector<int> expectedResults;
@@ -1671,18 +1840,19 @@ private Q_SLOTS:
channel.channelComponents[3].name = QLatin1String("Rotation Z");
dataType = static_cast<int>(QVariant::Quaternion);
+ componentCount = 4;
offset = 0;
expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
QTest::newRow("quaternion Rotation, offset = 0")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
QTest::newRow("quaternion Rotation, offset = 10")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
}
@@ -1697,18 +1867,19 @@ private Q_SLOTS:
channel.channelComponents[2].name = QLatin1String("Location Z");
dataType = static_cast<int>(QVariant::Vector3D);
+ componentCount = 3;
offset = 0;
expectedResults = (QVector<int>() << 0 << 1 << 2);
QTest::newRow("vec3 location, offset = 0")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
offset = 4;
expectedResults = (QVector<int>() << 4 << 5 << 6);
QTest::newRow("vec3 location, offset = 4")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
}
@@ -1723,18 +1894,19 @@ private Q_SLOTS:
channel.channelComponents[2].name = QLatin1String("Color B");
dataType = static_cast<int>(QVariant::Color);
+ componentCount = 3;
offset = 0;
expectedResults = (QVector<int>() << 0 << 1 << 2);
QTest::newRow("QColor Color, offset = 0")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
offset = 10;
expectedResults = (QVector<int>() << 10 << 11 << 12);
QTest::newRow("QColor Color, offset = 10")
- << channel << dataType << offset << expectedResults;
+ << channel << dataType << componentCount << offset << expectedResults;
expectedResults.clear();
}
@@ -1744,12 +1916,13 @@ private Q_SLOTS:
{
QFETCH(Channel, channel);
QFETCH(int, dataType);
+ QFETCH(int, componentCount);
QFETCH(int, offset);
QFETCH(QVector<int>, expectedResults);
// WHEN
QVector<int> actualResults
- = channelComponentsToIndices(channel, dataType, offset);
+ = channelComponentsToIndices(channel, dataType, componentCount, offset);
// THEN
QCOMPARE(actualResults.size(), expectedResults.size());
@@ -2387,7 +2560,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
QVector<ChannelMapping *> channelMappings;
channelMappings.push_back(channelMapping);
@@ -2397,6 +2571,7 @@ private Q_SLOTS:
QVector<ChannelNameAndType> expectedResults;
expectedResults.push_back({ QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() });
QTest::addRow("Location, vec3") << handler << channelMapper << expectedResults;
@@ -2409,13 +2584,15 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
auto channelMapping2 = createChannelMapping(handler,
QLatin1String("Rotation"),
Qt3DCore::QNodeId::createId(),
QLatin1String("rotatrion"),
"rotation",
- static_cast<int>(QVariant::Quaternion));
+ static_cast<int>(QVariant::Quaternion),
+ 4);
QVector<ChannelMapping *> channelMappings;
channelMappings.push_back(channelMapping1);
channelMappings.push_back(channelMapping2);
@@ -2429,9 +2606,11 @@ private Q_SLOTS:
QVector<ChannelNameAndType> expectedResults;
expectedResults.push_back({ QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping1->peerId() });
expectedResults.push_back({ QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
channelMapping2->peerId() });
QTest::addRow("Multiple unique channels") << handler << channelMapper << expectedResults;
@@ -2444,25 +2623,29 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
auto channelMapping2 = createChannelMapping(handler,
QLatin1String("Rotation"),
Qt3DCore::QNodeId::createId(),
QLatin1String("rotation"),
"rotation",
- static_cast<int>(QVariant::Quaternion));
+ static_cast<int>(QVariant::Quaternion),
+ 4);
auto channelMapping3 = createChannelMapping(handler,
QLatin1String("Location"),
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
auto channelMapping4 = createChannelMapping(handler,
QLatin1String("Location"),
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
QVector<ChannelMapping *> channelMappings;
channelMappings.push_back(channelMapping1);
@@ -2481,15 +2664,19 @@ private Q_SLOTS:
QVector<ChannelNameAndType> expectedResults;
expectedResults.push_back({ QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping1->peerId() });
expectedResults.push_back({ QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
channelMapping2->peerId() });
expectedResults.push_back({ QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping3->peerId() });
expectedResults.push_back({ QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping4->peerId() });
QTest::addRow("Multiple channels with repeats") << handler << channelMapper << expectedResults;
@@ -2510,6 +2697,7 @@ private Q_SLOTS:
for (int i = 0; i < jointCount; ++i) {
ChannelNameAndType locationDescription = { QLatin1String("Location"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
locationDescription.jointIndex = i;
locationDescription.jointTransformComponent = Translation;
@@ -2517,6 +2705,7 @@ private Q_SLOTS:
ChannelNameAndType rotationDescription = { QLatin1String("Rotation"),
static_cast<int>(QVariant::Quaternion),
+ 4,
channelMapping->peerId() };
rotationDescription.jointIndex = i;
rotationDescription.jointTransformComponent = Rotation;
@@ -2524,6 +2713,7 @@ private Q_SLOTS:
ChannelNameAndType scaleDescription = { QLatin1String("Scale"),
static_cast<int>(QVariant::Vector3D),
+ 3,
channelMapping->peerId() };
scaleDescription.jointIndex = i;
scaleDescription.jointTransformComponent = Scale;
@@ -2560,7 +2750,7 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> allChannels;
- allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
+ allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
QVector<ComponentIndices> expectedResults;
expectedResults.push_back({ 0, 1, 2 });
@@ -2570,8 +2760,8 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> allChannels;
- allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- allChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
+ allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ allChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
QVector<ComponentIndices> expectedResults;
expectedResults.push_back({ 0, 1, 2 });
@@ -2582,11 +2772,12 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> allChannels;
- allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- allChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
- allChannels.push_back({ QLatin1String("BaseColor"), static_cast<int>(QVariant::Vector3D) });
- allChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double) });
- allChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double) });
+ allChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ allChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
+ allChannels.push_back({ QLatin1String("BaseColor"), static_cast<int>(QVariant::Vector3D), 3 });
+ allChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
+ allChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
+ allChannels.push_back({ QLatin1String("MorphWeights"), static_cast<int>(QVariant::List), 6 });
QVector<ComponentIndices> expectedResults;
expectedResults.push_back({ 0, 1, 2 });
@@ -2594,23 +2785,24 @@ private Q_SLOTS:
expectedResults.push_back({ 7, 8, 9 });
expectedResults.push_back({ 10 });
expectedResults.push_back({ 11 });
+ expectedResults.push_back({ 12, 13, 14, 15, 16, 17 });
- QTest::newRow("vec3 location, quaterion rotation, pbr metal-rough") << allChannels << expectedResults;
+ QTest::newRow("vec3 location, quaterion rotation, pbr metal-rough morphweights") << allChannels << expectedResults;
}
{
QVector<ChannelNameAndType> allChannels;
const int jointCount = 4;
for (int i = 0; i < jointCount; ++i) {
- ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) };
+ ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 };
locationDescription.jointIndex = i;
allChannels.push_back(locationDescription);
- ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) };
+ ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 };
rotationDescription.jointIndex = i;
allChannels.push_back(rotationDescription);
- ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D) };
+ ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D), 3 };
scaleDescription.jointIndex = i;
allChannels.push_back(scaleDescription);
}
@@ -2665,11 +2857,11 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> targetChannels;
- targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
- targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double) });
- targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double) });
+ targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
+ targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
+ targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
QVector<ComponentIndices> targetIndices;
targetIndices.push_back({ 0, 1, 2, 3 });
@@ -2703,11 +2895,11 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> targetChannels;
- targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
- targetChannels.push_back({ QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double) });
- targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double) });
+ targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
+ targetChannels.push_back({ QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
+ targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
QVector<ComponentIndices> targetIndices;
targetIndices.push_back({ 0, 1, 2 });
@@ -2741,11 +2933,11 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> targetChannels;
- targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
- targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double) });
- targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double) });
+ targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
+ targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
+ targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
QVector<ComponentIndices> targetIndices;
targetIndices.push_back({ 0, 1, 2, 3 });
@@ -2779,11 +2971,11 @@ private Q_SLOTS:
{
QVector<ChannelNameAndType> targetChannels;
- targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) });
- targetChannels.push_back({ QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D) });
- targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double) });
- targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double) });
+ targetChannels.push_back({ QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
+ targetChannels.push_back({ QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D), 3 });
+ targetChannels.push_back({ QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
+ targetChannels.push_back({ QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
QVector<ComponentIndices> targetIndices;
targetIndices.push_back({ 0, 1, 2 });
@@ -2819,15 +3011,15 @@ private Q_SLOTS:
QVector<ChannelNameAndType> targetChannels;
const int jointCount = 4;
for (int i = 0; i < jointCount; ++i) {
- ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D) };
+ ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 };
locationDescription.jointIndex = i;
targetChannels.push_back(locationDescription);
- ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion) };
+ ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 };
rotationDescription.jointIndex = i;
targetChannels.push_back(rotationDescription);
- ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D) };
+ ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D), 3 };
scaleDescription.jointIndex = i;
targetChannels.push_back(scaleDescription);
}
@@ -2934,7 +3126,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
ChannelNameAndType channelDescription;
channelDescription.mappingId = channelMapping->peerId();
channelDescription.type = static_cast<int>(QVariant::Vector3D);
@@ -2950,7 +3143,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("rotation"),
"rotation",
- static_cast<int>(QVariant::Quaternion));
+ static_cast<int>(QVariant::Quaternion),
+ 4);
ChannelNameAndType channelDescription;
channelDescription.mappingId = channelMapping->peerId();
channelDescription.type = static_cast<int>(QVariant::Quaternion);
@@ -2966,7 +3160,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("scale"),
"scale",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
ChannelNameAndType channelDescription;
channelDescription.mappingId = channelMapping->peerId();
channelDescription.type = static_cast<int>(QVariant::Vector3D);
diff --git a/tests/auto/animation/channelmapping/tst_channelmapping.cpp b/tests/auto/animation/channelmapping/tst_channelmapping.cpp
index a947d4a2d..5c04c7f89 100644
--- a/tests/auto/animation/channelmapping/tst_channelmapping.cpp
+++ b/tests/auto/animation/channelmapping/tst_channelmapping.cpp
@@ -41,6 +41,18 @@
#include <Qt3DCore/private/qbackendnode_p.h>
#include "testpostmanarbiter.h"
+class tst_TargetEntity : public Qt3DCore::QEntity
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector2D foo MEMBER m_foo NOTIFY fooChanged)
+
+signals:
+ void fooChanged();
+
+private:
+ QVector2D m_foo;
+};
+
class tst_ChannelMapping : public Qt3DCore::QBackendNodeTester
{
Q_OBJECT
@@ -53,7 +65,7 @@ private Q_SLOTS:
Qt3DAnimation::Animation::ChannelMapping backendMapping;
backendMapping.setHandler(&handler);
Qt3DAnimation::QChannelMapping mapping;
- auto target = new Qt3DCore::QEntity;
+ auto target = new tst_TargetEntity;
mapping.setChannelName(QLatin1String("Location"));
mapping.setTarget(target);
@@ -68,6 +80,9 @@ private Q_SLOTS:
QCOMPARE(backendMapping.channelName(), mapping.channelName());
QCOMPARE(backendMapping.targetId(), mapping.target()->id());
QCOMPARE(backendMapping.property(), mapping.property());
+ QVERIFY(qstrcmp(backendMapping.propertyName(), "foo") == 0);
+ QCOMPARE(backendMapping.componentCount(), 2);
+ QCOMPARE(backendMapping.type(), static_cast<int>(QVariant::Vector2D));
QCOMPARE(backendMapping.mappingType(), Qt3DAnimation::Animation::ChannelMapping::ChannelMappingType);
// GIVEN
@@ -100,12 +115,15 @@ private Q_SLOTS:
QCOMPARE(backendMapping.channelName(), QString());
QCOMPARE(backendMapping.targetId(), Qt3DCore::QNodeId());
QCOMPARE(backendMapping.property(), QString());
+ QCOMPARE(backendMapping.propertyName(), nullptr);
+ QCOMPARE(backendMapping.componentCount(), 0);
+ QCOMPARE(backendMapping.type(), static_cast<int>(QVariant::Invalid));
QCOMPARE(backendMapping.skeletonId(), Qt3DCore::QNodeId());
QCOMPARE(backendMapping.mappingType(), Qt3DAnimation::Animation::ChannelMapping::ChannelMappingType);
// GIVEN
Qt3DAnimation::QChannelMapping mapping;
- auto target = new Qt3DCore::QEntity;
+ auto target = new tst_TargetEntity;
mapping.setChannelName(QLatin1String("Location"));
mapping.setTarget(target);
mapping.setProperty(QLatin1String("foo"));
@@ -120,6 +138,9 @@ private Q_SLOTS:
QCOMPARE(backendMapping.channelName(), QString());
QCOMPARE(backendMapping.targetId(), Qt3DCore::QNodeId());
QCOMPARE(backendMapping.property(), QString());
+ QCOMPARE(backendMapping.propertyName(), nullptr);
+ QCOMPARE(backendMapping.componentCount(), 0);
+ QCOMPARE(backendMapping.type(), static_cast<int>(QVariant::Invalid));
QCOMPARE(backendMapping.skeletonId(), Qt3DCore::QNodeId());
QCOMPARE(backendMapping.mappingType(), Qt3DAnimation::Animation::ChannelMapping::ChannelMappingType);
}
@@ -172,6 +193,34 @@ private Q_SLOTS:
QCOMPARE(backendMapping.property(), property);
// WHEN
+ updateChange = QSharedPointer<Qt3DCore::QPropertyUpdatedChange>::create(Qt3DCore::QNodeId());
+ updateChange->setPropertyName("type");
+ updateChange->setValue(QVariant(static_cast<int>(QVariant::Vector3D)));
+ backendMapping.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(backendMapping.type(), static_cast<int>(QVariant::Vector3D));
+
+ // WHEN
+ updateChange = QSharedPointer<Qt3DCore::QPropertyUpdatedChange>::create(Qt3DCore::QNodeId());
+ updateChange->setPropertyName("componentCount");
+ updateChange->setValue(4);
+ backendMapping.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(backendMapping.componentCount(), 4);
+
+ // WHEN
+ const char *testName = "883";
+ updateChange = QSharedPointer<Qt3DCore::QPropertyUpdatedChange>::create(Qt3DCore::QNodeId());
+ updateChange->setPropertyName("propertyName");
+ updateChange->setValue(QVariant::fromValue(reinterpret_cast<void *>(const_cast<char *>(testName))));
+ backendMapping.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(backendMapping.propertyName(), testName);
+
+ // WHEN
const auto skeletonId = Qt3DCore::QNodeId::createId();
updateChange = QSharedPointer<Qt3DCore::QPropertyUpdatedChange>::create(Qt3DCore::QNodeId());
updateChange->setPropertyName("skeleton");
diff --git a/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp b/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
index 50c0eb524..cc09c7941 100644
--- a/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
+++ b/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
@@ -61,7 +61,8 @@ public:
const Qt3DCore::QNodeId targetId,
const QString &property,
const char *propertyName,
- int type)
+ int type,
+ int componentCount)
{
auto channelMappingId = Qt3DCore::QNodeId::createId();
ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(channelMappingId);
@@ -72,6 +73,7 @@ public:
channelMapping->setPropertyName(propertyName);
channelMapping->setChannelName(channelName);
channelMapping->setType(type);
+ channelMapping->setComponentCount(componentCount);
channelMapping->setMappingType(ChannelMapping::ChannelMappingType);
return channelMapping;
}
@@ -143,7 +145,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
QVector<ChannelMapping *> channelMappings;
channelMappings.push_back(channelMapping);
@@ -188,7 +191,8 @@ private Q_SLOTS:
Qt3DCore::QNodeId::createId(),
QLatin1String("translation"),
"translation",
- static_cast<int>(QVariant::Vector3D));
+ static_cast<int>(QVariant::Vector3D),
+ 3);
QVector<ChannelMapping *> channelMappings;
channelMappings.push_back(channelMapping);
@@ -198,9 +202,9 @@ private Q_SLOTS:
animator->setEnabled(false); // Has to be marked as enabled for the job to process it
QTest::newRow("disabled animator")
- << handler
- << dirtyClipAnimators
- << expectedResults;
+ << handler
+ << dirtyClipAnimators
+ << expectedResults;
}
}
diff --git a/tests/auto/animation/qchannelmapping/tst_qchannelmapping.cpp b/tests/auto/animation/qchannelmapping/tst_qchannelmapping.cpp
index cab5fda6f..091876d09 100644
--- a/tests/auto/animation/qchannelmapping/tst_qchannelmapping.cpp
+++ b/tests/auto/animation/qchannelmapping/tst_qchannelmapping.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QTest>
#include <Qt3DAnimation/qchannelmapping.h>
+#include <Qt3DAnimation/private/qabstractchannelmapping_p.h>
#include <Qt3DAnimation/private/qchannelmapping_p.h>
#include <Qt3DAnimation/private/qchannelmappingcreatedchange_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
@@ -36,8 +37,49 @@
#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
#include <QObject>
#include <QSignalSpy>
+#include <QQuaternion>
#include <testpostmanarbiter.h>
+class tst_QTargetEntity : public Qt3DCore::QEntity
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuaternion rotation MEMBER m_rotation NOTIFY rotationChanged)
+ Q_PROPERTY(QVector3D translation MEMBER m_translation NOTIFY translationChanged)
+ Q_PROPERTY(QVector3D scale MEMBER m_scale NOTIFY scaleChanged)
+ Q_PROPERTY(float floatProperty MEMBER m_floatProperty NOTIFY floatPropertyChanged)
+ Q_PROPERTY(QVector2D vec2Property MEMBER m_vec2Property NOTIFY vec2PropertyChanged)
+ Q_PROPERTY(QVector3D vec3Property MEMBER m_vec3Property NOTIFY vec3PropertyChanged)
+ Q_PROPERTY(QVector4D vec4Property MEMBER m_vec4Property NOTIFY vec4PropertyChanged)
+ Q_PROPERTY(QQuaternion quaternionProperty MEMBER m_quaternionProperty NOTIFY quaternionPropertyChanged)
+ Q_PROPERTY(QVariantList listProperty MEMBER m_listProperty NOTIFY listPropertyChanged)
+ Q_PROPERTY(QVector<float> vecProperty MEMBER m_vecProperty NOTIFY vecPropertyChanged)
+
+signals:
+ void rotationChanged();
+ void translationChanged();
+ void scaleChanged();
+ void floatPropertyChanged();
+ void vec2PropertyChanged();
+ void vec3PropertyChanged();
+ void vec4PropertyChanged();
+ void quaternionPropertyChanged();
+ void listPropertyChanged();
+ void vecPropertyChanged();
+
+private:
+ QQuaternion m_rotation;
+ QVector3D m_translation;
+ QVector3D m_scale;
+ float m_floatProperty;
+ QVector2D m_vec2Property;
+ QVector3D m_vec3Property;
+ QVector4D m_vec4Property;
+ QQuaternion m_quaternionProperty;
+ QVariantList m_listProperty;
+ QVector<float> m_vecProperty;
+};
+
+
class tst_QChannelMapping : public QObject
{
Q_OBJECT
@@ -52,6 +94,13 @@ private Q_SLOTS:
QCOMPARE(mapping.channelName(), QString());
QCOMPARE(mapping.target(), static_cast<Qt3DCore::QNode *>(nullptr));
QCOMPARE(mapping.property(), QString());
+
+ const Qt3DAnimation::QChannelMappingPrivate *d =
+ static_cast<const Qt3DAnimation::QChannelMappingPrivate *>(
+ Qt3DAnimation::QChannelMappingPrivate::get(&mapping));
+
+ QCOMPARE(d->m_type, static_cast<int>(QVariant::Invalid));
+ QCOMPARE(d->m_componentCount, 0);
}
void checkPropertyChanges()
@@ -125,7 +174,7 @@ private Q_SLOTS:
{
// GIVEN
Qt3DAnimation::QChannelMapping mapping;
- auto target = new Qt3DCore::QEntity;
+ auto target = new tst_QTargetEntity;
mapping.setChannelName(QStringLiteral("Location"));
mapping.setTarget(target);
@@ -154,6 +203,8 @@ private Q_SLOTS:
QCOMPARE(mapping.channelName(), data.channelName);
QCOMPARE(mapping.target()->id(), data.targetId);
QCOMPARE(mapping.property(), data.property);
+ QCOMPARE(data.type, static_cast<int>(QVariant::Vector3D));
+ QCOMPARE(data.componentCount, 3);
}
// WHEN
@@ -179,6 +230,8 @@ private Q_SLOTS:
QCOMPARE(mapping.channelName(), data.channelName);
QCOMPARE(mapping.target()->id(), data.targetId);
QCOMPARE(mapping.property(), data.property);
+ QCOMPARE(data.type, static_cast<int>(QVariant::Vector3D));
+ QCOMPARE(data.componentCount, 3);
}
}
@@ -187,6 +240,7 @@ private Q_SLOTS:
// GIVEN
TestArbiter arbiter;
Qt3DAnimation::QChannelMapping mapping;
+ QScopedPointer<Qt3DCore::QEntity> target(new tst_QTargetEntity());
arbiter.setArbiterOnNode(&mapping);
{
@@ -213,8 +267,7 @@ private Q_SLOTS:
{
// WHEN
- auto target = new Qt3DCore::QEntity();
- mapping.setTarget(target);
+ mapping.setTarget(target.data());
QCoreApplication::processEvents();
// THEN
@@ -227,7 +280,7 @@ private Q_SLOTS:
arbiter.events.clear();
// WHEN
- mapping.setTarget(target);
+ mapping.setTarget(target.data());
QCoreApplication::processEvents();
// THEN
@@ -236,16 +289,32 @@ private Q_SLOTS:
{
// WHEN
+ target->setProperty("scale", QVector3D(1.0f, 0.0f, 0.0f));
mapping.setProperty(QStringLiteral("scale"));
QCoreApplication::processEvents();
// THEN
- QCOMPARE(arbiter.events.size(), 1);
- auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(arbiter.events.size(), 4);
+ auto change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
QCOMPARE(change->propertyName(), "property");
QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
QCOMPARE(change->value().toString(), mapping.property());
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "type");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toInt(), static_cast<int>(QVariant::Vector3D));
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "componentCount");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toInt(), 3);
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "propertyName");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QVERIFY(qstrcmp(reinterpret_cast<const char *>(change->value().value<void *>()), "scale") == 0);
+
arbiter.events.clear();
// WHEN
@@ -255,8 +324,88 @@ private Q_SLOTS:
// THEN
QCOMPARE(arbiter.events.size(), 0);
}
+ }
+ void checkPropertyUpdateNameTypeAndComponentCount_data()
+ {
+ QTest::addColumn<QByteArray>("propertyName");
+ QTest::addColumn<QVariant>("value");
+ QTest::addColumn<int>("expectedType");
+ QTest::addColumn<int>("expectedComponentCount");
+
+ QTest::newRow("float") << QByteArrayLiteral("floatProperty") << QVariant(1.0f) << static_cast<int>(QMetaType::Float) << 1;
+ QTest::newRow("vec2") << QByteArrayLiteral("vec2Property") << QVariant(QVector2D(1.0f, 1.0f)) << static_cast<int>(QVariant::Vector2D) << 2;
+ QTest::newRow("vec3") << QByteArrayLiteral("vec3Property") << QVariant(QVector3D(1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Vector3D) << 3;
+ QTest::newRow("vec4") << QByteArrayLiteral("vec4Property") << QVariant(QVector4D(1.0f, 1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Vector4D) << 4;
+ QTest::newRow("quaternion") << QByteArrayLiteral("quaternionProperty") << QVariant(QQuaternion(1.0f, 1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Quaternion) << 4;
+
+ QVariantList list = QVariantList() << QVariant(1.0f) << QVariant(1.0) << QVariant(1.0f) << QVariant(1.0f) << QVariant(1.0f);
+ QTest::newRow("variantlist") << QByteArrayLiteral("listProperty") << QVariant::fromValue(list) << static_cast<int>(QVariant::List) << 5;
+
+ QVector<float> vec(8);
+ QTest::newRow("vector") << QByteArrayLiteral("vecProperty") << QVariant::fromValue(vec) << qMetaTypeId<decltype(vec)>() << 8;
}
+
+ void checkPropertyUpdateNameTypeAndComponentCount()
+ {
+ // GIVEN
+ QFETCH(QByteArray, propertyName);
+ QFETCH(QVariant, value);
+ QFETCH(int, expectedType);
+ QFETCH(int, expectedComponentCount);
+
+ TestArbiter arbiter;
+ Qt3DAnimation::QChannelMapping mapping;
+ QScopedPointer<Qt3DCore::QEntity> target(new tst_QTargetEntity());
+ mapping.setTarget(target.data());
+ arbiter.setArbiterOnNode(&mapping);
+
+ {
+ // WHEN
+ target->setProperty(propertyName.constData(), value);
+ mapping.setProperty(QString::fromLatin1(propertyName));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 5);
+
+ // Automatic notification change when property is updated
+ auto change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), propertyName.constData());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value(), value);
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "property");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toString(), mapping.property());
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "type");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toInt(), expectedType);
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "componentCount");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toInt(), expectedComponentCount);
+
+ change = arbiter.events.takeFirst().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "propertyName");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QVERIFY(qstrcmp(reinterpret_cast<const char *>(change->value().value<void *>()), propertyName.constData()) == 0);
+
+ arbiter.events.clear();
+
+ // WHEN
+ mapping.setProperty(QString::fromLatin1(propertyName));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+
};
QTEST_MAIN(tst_QChannelMapping)
diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp
index 3f7fb4a75..dad66c5d5 100644
--- a/tests/auto/core/nodes/tst_nodes.cpp
+++ b/tests/auto/core/nodes/tst_nodes.cpp
@@ -73,6 +73,7 @@ private slots:
void checkParentChangeToNull();
void checkParentChangeToOtherParent();
void checkParentChangeFromExistingBackendParentToNewlyCreatedParent();
+ void checkBackendNodesCreatedFromTopDown(); //QTBUG-74106
void removingSingleChildNodeFromNode();
void removingMultipleChildNodesFromNode();
@@ -81,6 +82,7 @@ private slots:
void removingChildEntitiesFromNode();
void checkConstructionSetParentMix(); // QTBUG-60612
+ void checkParentingQEntityToQNode(); // QTBUG-73905
void checkConstructionWithParent();
void checkConstructionWithNonRootParent(); // QTBUG-73986
void checkConstructionAsListElement();
@@ -901,6 +903,85 @@ void tst_Nodes::checkParentChangeFromExistingBackendParentToNewlyCreatedParent()
}
}
+//Test creation changes happen for an entire subtree at once, starting at the top
+// so that parents are always created before their children. Even if the front-end
+// nodes are constructed in a different order.
+void tst_Nodes::checkBackendNodesCreatedFromTopDown()
+{
+ // GIVEN
+ Qt3DCore::QScene scene;
+ ObserverSpy spy;
+ QScopedPointer<MyQNode> root(new MyQNode());
+ root->setArbiterAndScene(&spy, &scene);
+ QScopedPointer<Qt3DCore::QNode> parentWithBackend(new MyQNode(root.data()));
+
+ // create parent backend node
+ QCoreApplication::processEvents();
+ QVERIFY(Qt3DCore::QNodePrivate::get(parentWithBackend.get())->m_hasBackendNode);
+
+ // WHEN -> creating 3 nodes and setting one as a property on the other
+ // child2 is set as property on child1, but is created AFTER child1
+ spy.events.clear();
+ MyQNode *dummyParent(new MyQNode(parentWithBackend.data()));
+ MyQNode *child1(new MyQNode(parentWithBackend.data()));
+ MyQNode *child2(new MyQNode(dummyParent));
+ child2->setNodeProperty(child1);
+
+ // THEN - we should have no events because the new nodes have no backend yet
+ QCOMPARE(spy.events.count(), 0);
+
+ // WHEN - create the backend nodes
+ QCoreApplication::processEvents();
+
+ // THEN
+ QVERIFY(dummyParent->parent() == parentWithBackend.data());
+ QVERIFY(child1->parent() == parentWithBackend.data());
+ QVERIFY(child2->parent() == dummyParent);
+
+ // THEN
+ QCOMPARE(spy.events.size(), 5);
+ // 2 node creation change for dummyParent subtree (dummyParent and child2)
+ // 1 node added to children change (dummyParent to parent)
+ // 1 node created change for child1
+ // 1 node added to children change (child1 to parent)
+
+ {
+ // 1st event: dummyParent creation
+ const auto event1 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!event1.isNull());
+ QCOMPARE(event1->type(), Qt3DCore::NodeCreated);
+ QCOMPARE(event1->parentId(), parentWithBackend->id());
+ QCOMPARE(event1->subjectId(), dummyParent->id());
+
+ // 2nd event: child2 creation (even though we constructed child1 first)
+ const auto event2 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!event2.isNull());
+ QCOMPARE(event2->type(), Qt3DCore::NodeCreated);
+ QCOMPARE(event2->parentId(), dummyParent->id());
+ QCOMPARE(event2->subjectId(), child2->id());
+
+ // 3rd event: dummyParent added to parent
+ const auto event3 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
+ QCOMPARE(event3->type(), Qt3DCore::PropertyValueAdded);
+ QCOMPARE(event3->addedNodeId(), dummyParent->id());
+ QCOMPARE(event3->subjectId(), parentWithBackend->id());
+
+ // 4th event: child1 creation
+ const auto event4 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!event4.isNull());
+ QCOMPARE(event4->type(), Qt3DCore::NodeCreated);
+ QCOMPARE(event4->parentId(), parentWithBackend->id());
+ QCOMPARE(event4->subjectId(), child1->id());
+
+ // 5th event: child 1 added to parent
+ const auto event5 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
+ QCOMPARE(event5->type(), Qt3DCore::PropertyValueAdded);
+ QCOMPARE(event5->addedNodeId(), child1->id());
+ QCOMPARE(event5->subjectId(), parentWithBackend->id());
+ }
+}
+
+
void tst_Nodes::removingSingleChildNodeFromNode()
{
// GIVEN
@@ -1079,6 +1160,72 @@ void tst_Nodes::checkConstructionSetParentMix()
QCOMPARE(lastEvent->addedNodeId(), subTreeRoot->id());
}
+void tst_Nodes::checkParentingQEntityToQNode()
+{
+ // 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
+ auto subTreeRoot = new Qt3DCore::QEntity(root.data());
+ auto childEntity = new Qt3DCore::QEntity(subTreeRoot);
+ auto childNode = new Qt3DCore::QNode(subTreeRoot);
+
+ // THEN
+ QCoreApplication::processEvents();
+
+ // Ensure first event is subTreeRoot creation
+ const Qt3DCore::QNodeCreatedChangeBasePtr firstEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!firstEvent.isNull());
+ QCOMPARE(firstEvent->subjectId(), subTreeRoot->id());
+ QCOMPARE(firstEvent->parentId(), root->id());
+
+ // Ensure 2nd event is childEntity creation
+ const Qt3DCore::QNodeCreatedChangeBasePtr secondEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!secondEvent.isNull());
+ QCOMPARE(secondEvent->subjectId(), childEntity->id());
+ QCOMPARE(secondEvent->parentId(), subTreeRoot->id());
+
+ // Ensure 3rd event is childNode creation
+ const Qt3DCore::QNodeCreatedChangeBasePtr thirdEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!thirdEvent.isNull());
+ QCOMPARE(thirdEvent->subjectId(), childNode->id());
+ QCOMPARE(thirdEvent->parentId(), subTreeRoot->id());
+
+
+ // WHEN we reparent the childEntity to the childNode (QNode)
+
+ spy.events.clear();
+ childEntity->setParent(childNode);
+ // THEN we should get
+ // - one child removed change for childEntity->subTreeRoot,
+ // - one child added change for childEntity->childNode,
+ // - and one property updated event specifying the correct QEntity parent (subTreeRoot)
+ QCOMPARE(spy.events.size(), 3);
+
+ const auto removedEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>();
+ QVERIFY(!removedEvent.isNull());
+ QCOMPARE(removedEvent->subjectId(), subTreeRoot->id());
+
+ const auto addedEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
+ QVERIFY(!addedEvent.isNull());
+ QCOMPARE(addedEvent->subjectId(), childNode->id());
+
+ const auto parentChangeEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyUpdatedChange>();
+ QVERIFY(!parentChangeEvent.isNull());
+ QCOMPARE(parentChangeEvent->subjectId(), childEntity->id());
+ QCOMPARE(parentChangeEvent->propertyName(), "parentEntityUpdated");
+ QCOMPARE(parentChangeEvent->value().value<Qt3DCore::QNodeId>(), subTreeRoot->id());
+}
+
void tst_Nodes::checkConstructionWithParent()
{
// GIVEN
diff --git a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp
index 992e643d2..b35c6d31a 100644
--- a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp
+++ b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp
@@ -55,6 +55,7 @@
#include <Qt3DRender/private/calcboundingvolumejob_p.h>
#include <Qt3DRender/private/calcgeometrytrianglevolumes_p.h>
#include <Qt3DRender/private/loadbufferjob_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/private/buffermanager_p.h>
#include <Qt3DRender/private/geometryrenderermanager_p.h>
#include <Qt3DRender/private/sphere_p.h>
@@ -116,6 +117,10 @@ namespace {
void runRequiredJobs(Qt3DRender::TestAspect *test)
{
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(test->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
updateWorldTransform.setRoot(test->sceneRoot());
updateWorldTransform.run();
diff --git a/tests/auto/render/entity/tst_entity.cpp b/tests/auto/render/entity/tst_entity.cpp
index 6ad958451..123a648d6 100644
--- a/tests/auto/render/entity/tst_entity.cpp
+++ b/tests/auto/render/entity/tst_entity.cpp
@@ -151,7 +151,7 @@ private slots:
QVERIFY(!entity.componentsUuid<EnvironmentLight>().isEmpty());
QVERIFY(!entity.componentUuid<Armature>().isNull());
QVERIFY(entity.isBoundingVolumeDirty());
- QVERIFY(!entity.childrenHandles().isEmpty());
+ QVERIFY(entity.childrenHandles().isEmpty());
QVERIFY(!entity.layerIds().isEmpty());
QVERIFY(renderer.dirtyBits() != 0);
bool containsAll = entity.containsComponentsOfType<Transform,
@@ -162,6 +162,7 @@ private slots:
entity.cleanup();
// THEN
+ QVERIFY(entity.parentEntityId().isNull());
QVERIFY(entity.componentUuid<Transform>().isNull());
QVERIFY(entity.componentUuid<CameraLens>().isNull());
QVERIFY(entity.componentUuid<Material>().isNull());
@@ -180,6 +181,122 @@ private slots:
QVERIFY(!containsAll);
}
+ void checkRebuildingEntityHierarchy()
+ {
+ // GIVEN
+ TestRenderer renderer;
+ NodeManagers nodeManagers;
+ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
+
+ auto entityCreator = [&nodeManagers, &renderer](const Qt3DCore::QEntity &frontEndEntity) {
+ Entity *entity = nodeManagers.renderNodesManager()->getOrCreateResource(frontEndEntity.id());
+ entity->setNodeManagers(&nodeManagers);
+ entity->setRenderer(&renderer);
+ return entity;
+ };
+
+ auto backendA = entityCreator(frontendEntityA);
+ auto backendB = entityCreator(frontendEntityB);
+ auto backendC = entityCreator(frontendEntityC);
+
+ // THEN
+ QVERIFY(backendA->parentEntityId().isNull());
+ QVERIFY(backendB->parentEntityId().isNull());
+ QVERIFY(backendC->parentEntityId().isNull());
+
+ QVERIFY(backendA->parent() == nullptr);
+ QVERIFY(backendB->parent() == nullptr);
+ QVERIFY(backendC->parent() == nullptr);
+
+ QVERIFY(backendA->childrenHandles().isEmpty());
+ QVERIFY(backendB->childrenHandles().isEmpty());
+ QVERIFY(backendC->childrenHandles().isEmpty());
+
+ // WHEN
+ renderer.clearDirtyBits(0);
+ QVERIFY(renderer.dirtyBits() == 0);
+
+ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
+ const auto parentChange = QPropertyUpdatedChangePtr::create(entity.id());
+ parentChange->setPropertyName("parentEntityUpdated");
+ auto parent = entity.parentEntity();
+ parentChange->setValue(QVariant::fromValue(parent ? parent->id() : Qt3DCore::QNodeId()));
+
+ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
+ backendEntity->sceneChangeEvent(parentChange);
+ };
+
+ // reparent B to A and C to B.
+ frontendEntityB.setParent(&frontendEntityA);
+ sendParentChange(frontendEntityB);
+ frontendEntityC.setParent(&frontendEntityB);
+ sendParentChange(frontendEntityC);
+
+ // THEN
+ QVERIFY(renderer.dirtyBits() & AbstractRenderer::EntityHierarchyDirty);
+
+ QVERIFY(backendA->parentEntityId().isNull());
+ QVERIFY(backendB->parentEntityId() == frontendEntityA.id());
+ QVERIFY(backendC->parentEntityId() == frontendEntityB.id());
+
+ QVERIFY(backendA->parent() == nullptr);
+ QVERIFY(backendB->parent() == nullptr);
+ QVERIFY(backendC->parent() == nullptr);
+
+ QVERIFY(backendA->childrenHandles().isEmpty());
+ QVERIFY(backendB->childrenHandles().isEmpty());
+ QVERIFY(backendC->childrenHandles().isEmpty());
+
+ // WHEN
+ auto rebuildHierarchy = [](Entity *backend) {
+ backend->clearEntityHierarchy();
+ backend->rebuildEntityHierarchy();
+ };
+ rebuildHierarchy(backendA);
+ rebuildHierarchy(backendB);
+ rebuildHierarchy(backendC);
+
+ // THEN
+ QVERIFY(backendA->parent() == nullptr);
+ QVERIFY(backendB->parent() == backendA);
+ QVERIFY(backendC->parent() == backendB);
+
+ QVERIFY(!backendA->childrenHandles().isEmpty());
+ QVERIFY(!backendB->childrenHandles().isEmpty());
+ QVERIFY(backendC->childrenHandles().isEmpty());
+
+ // WHEN - reparent B to null.
+ frontendEntityB.setParent(static_cast<Qt3DCore::QNode *>(nullptr));
+ sendParentChange(frontendEntityB);
+ rebuildHierarchy(backendA);
+ rebuildHierarchy(backendB);
+ rebuildHierarchy(backendC);
+
+ QVERIFY(backendA->parentEntityId().isNull());
+ QVERIFY(backendB->parentEntityId().isNull());
+ QVERIFY(backendC->parentEntityId() == frontendEntityB.id());
+
+ QVERIFY(backendA->parent() == nullptr);
+ QVERIFY(backendB->parent() == nullptr);
+ QVERIFY(backendC->parent() == backendB);
+
+ QVERIFY(backendA->childrenHandles().isEmpty());
+ QVERIFY(!backendB->childrenHandles().isEmpty());
+ QVERIFY(backendC->childrenHandles().isEmpty());
+
+ // WHEN - cleanup
+ backendA->cleanup();
+ backendB->cleanup();
+ backendC->cleanup();
+
+ // THEN
+ QVERIFY(backendA->parentEntityId().isNull());
+ QVERIFY(backendB->parentEntityId().isNull());
+ QVERIFY(backendC->parentEntityId().isNull());
+
+ QVERIFY(renderer.dirtyBits() != 0);
+ }
+
void shouldHandleSingleComponentEvents_data()
{
QTest::addColumn<QComponent*>("component");
diff --git a/tests/auto/render/framegraphnode/tst_framegraphnode.cpp b/tests/auto/render/framegraphnode/tst_framegraphnode.cpp
index 07ff4c0d9..22bd872dc 100644
--- a/tests/auto/render/framegraphnode/tst_framegraphnode.cpp
+++ b/tests/auto/render/framegraphnode/tst_framegraphnode.cpp
@@ -30,8 +30,7 @@
#include <QtTest/QTest>
#include <Qt3DRender/private/managers_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
-#include <Qt3DCore/qpropertynodeaddedchange.h>
-#include <Qt3DCore/qpropertynoderemovedchange.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
#include "testrenderer.h"
@@ -108,24 +107,9 @@ private Q_SLOTS:
// WHEN
n->setParentId(parentId);
- // THEN
- QCOMPARE(n->parentId(), parentId);
-
- // WHEN
- const Qt3DCore::QNodeId childId = Qt3DCore::QNodeId::createId();
- QScopedPointer<Qt3DRender::Render::FrameGraphNode> c(new MyFrameGraphNode());
- setIdInternal(c.data(), childId);
- manager->appendNode(childId, c.data());
- n->appendChildId(childId);
- // THEN
- QCOMPARE(n->childrenIds().count(), 1);
- // WHEN
- n->appendChildId(childId);
// THEN
- QCOMPARE(n->childrenIds().count(), 1);
-
- c.take();
+ QCOMPARE(n->parentId(), parentId);
}
void checkParentChange()
@@ -151,17 +135,8 @@ private Q_SLOTS:
QVERIFY(child->parentId().isNull());
// WHEN
- parent1->appendChildId(childId);
- // THEN
- QCOMPARE(child->parentId(), parentId);
- QCOMPARE(child->parent(), parent1);
- QCOMPARE(parent1->childrenIds().count(), 1);
- QCOMPARE(parent1->childrenIds().first(), childId);
- QCOMPARE(parent1->children().count(), parent1->childrenIds().count());
- QCOMPARE(parent1->children().first(), child);
+ child->setParentId(parentId);
- // WHEN
- parent1->appendChildId(childId);
// THEN
QCOMPARE(child->parentId(), parentId);
QCOMPARE(child->parent(), parent1);
@@ -171,7 +146,8 @@ private Q_SLOTS:
QCOMPARE(parent1->children().first(), child);
// WHEN
- parent1->removeChildId(childId);
+ child->setParentId(Qt3DCore::QNodeId());
+
// THEN
QVERIFY(child->parentId().isNull());
QVERIFY(child->parent() == nullptr);
@@ -251,6 +227,7 @@ private Q_SLOTS:
TestRenderer renderer;
backendFGNode->setRenderer(&renderer);
+ backendFGChild->setRenderer(&renderer);
setIdInternal(backendFGNode, fgNode1Id);
setIdInternal(backendFGChild, frontendFGChild->id());
@@ -269,20 +246,27 @@ private Q_SLOTS:
{
// WHEN
- const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(Qt3DCore::QNodeId(), frontendFGChild);
- backendFGNode->sceneChangeEvent(change);
+ renderer.clearDirtyBits(0);
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(frontendFGChild->id());
+ change->setPropertyName("parentFrameGraphUpdated");
+ change->setValue(QVariant::fromValue(fgNode1Id));
+ backendFGChild->sceneChangeEvent(change);
// THEN
QCOMPARE(backendFGNode->childrenIds().size(), 1);
- QCOMPARE(backendFGNode->childrenIds().first(), frontendFGChild->id());
+ QCOMPARE(backendFGChild->parentId(), fgNode1Id);
}
{
// WHEN
- const auto change = Qt3DCore::QPropertyNodeRemovedChangePtr::create(Qt3DCore::QNodeId(), frontendFGChild);
- backendFGNode->sceneChangeEvent(change);
+ renderer.clearDirtyBits(0);
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(frontendFGChild->id());
+ change->setPropertyName("parentFrameGraphUpdated");
+ change->setValue(QVariant::fromValue(Qt3DCore::QNodeId()));
+ backendFGChild->sceneChangeEvent(change);
// THEN
QCOMPARE(backendFGNode->childrenIds().size(), 0);
+ QVERIFY(backendFGChild->parentId().isNull());
}
}
diff --git a/tests/auto/render/layerfiltering/tst_layerfiltering.cpp b/tests/auto/render/layerfiltering/tst_layerfiltering.cpp
index eeffc69b2..9b8636f49 100644
--- a/tests/auto/render/layerfiltering/tst_layerfiltering.cpp
+++ b/tests/auto/render/layerfiltering/tst_layerfiltering.cpp
@@ -33,6 +33,7 @@
#include <Qt3DRender/private/entity_p.h>
#include <Qt3DRender/private/filterlayerentityjob_p.h>
#include <Qt3DRender/private/updatetreeenabledjob_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/qlayer.h>
#include <Qt3DRender/qlayerfilter.h>
#include "testaspect.h"
@@ -632,6 +633,11 @@ private Q_SLOTS:
// WHEN
Qt3DRender::Render::Entity *backendRoot = aspect->nodeManagers()->renderNodesManager()->getOrCreateResource(entitySubtree->id());
+
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(aspect->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateTreeEnabledJob updateTreeEnabledJob;
updateTreeEnabledJob.setRoot(backendRoot);
updateTreeEnabledJob.run();
diff --git a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
index e2acff16c..cd82d69d8 100644
--- a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
+++ b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
@@ -48,6 +48,7 @@
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/pickboundingvolumejob_p.h>
#include <Qt3DRender/private/pickboundingvolumeutils_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h>
#include <Qt3DRender/private/updateworldboundingvolumejob_p.h>
#include <Qt3DRender/private/updateworldtransformjob_p.h>
@@ -110,6 +111,10 @@ namespace {
void runRequiredJobs(Qt3DRender::TestAspect *test)
{
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(test->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
updateWorldTransform.setRoot(test->sceneRoot());
updateWorldTransform.run();
diff --git a/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp b/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp
index 7a5648271..7bb3c16a7 100644
--- a/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp
+++ b/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp
@@ -246,6 +246,11 @@ private Q_SLOTS:
// WHEN
Qt3DRender::Render::Entity *backendRoot = aspect->nodeManagers()->renderNodesManager()->getOrCreateResource(entitySubtree->id());
+
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(aspect->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateTreeEnabledJob updateTreeEnabledJob;
updateTreeEnabledJob.setRoot(backendRoot);
updateTreeEnabledJob.run();
diff --git a/tests/auto/render/qcamera/tst_qcamera.cpp b/tests/auto/render/qcamera/tst_qcamera.cpp
index b630c447a..7aef2af7d 100644
--- a/tests/auto/render/qcamera/tst_qcamera.cpp
+++ b/tests/auto/render/qcamera/tst_qcamera.cpp
@@ -35,6 +35,7 @@
#include <Qt3DCore/QEntity>
#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DCore/private/qaspectjobmanager_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qnodecreatedchange.h>
@@ -99,6 +100,10 @@ namespace {
void runRequiredJobs(Qt3DRender::TestAspect *test)
{
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(test->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
updateWorldTransform.setRoot(test->sceneRoot());
updateWorldTransform.run();
diff --git a/tests/auto/render/raycastingjob/tst_raycastingjob.cpp b/tests/auto/render/raycastingjob/tst_raycastingjob.cpp
index 4980bfc30..411bb9160 100644
--- a/tests/auto/render/raycastingjob/tst_raycastingjob.cpp
+++ b/tests/auto/render/raycastingjob/tst_raycastingjob.cpp
@@ -51,6 +51,7 @@
#include <Qt3DRender/private/updateworldtransformjob_p.h>
#include <Qt3DRender/private/expandboundingvolumejob_p.h>
#include <Qt3DRender/private/calcboundingvolumejob_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/private/calcgeometrytrianglevolumes_p.h>
#include <Qt3DRender/private/loadbufferjob_p.h>
#include <Qt3DRender/private/buffermanager_p.h>
@@ -106,6 +107,10 @@ namespace {
void runRequiredJobs(Qt3DRender::TestAspect *test)
{
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(test->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
updateWorldTransform.setRoot(test->sceneRoot());
updateWorldTransform.run();
diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp
index 012cdb1b1..a823c233a 100644
--- a/tests/auto/render/renderer/tst_renderer.cpp
+++ b/tests/auto/render/renderer/tst_renderer.cpp
@@ -254,8 +254,8 @@ private Q_SLOTS:
1 + // updateSkinningPaletteJob
1 + // SyncLoadingJobs
1 + // ExpandBoundingVolumeJob
- 1 + // RenderableEntityFilterJob
- 1 + // CacheRenderableEntitiesJob
+ 1 + // RenderableEntityFilterPtr
+ 1 + // SyncRenderableEntities
singleRenderViewJobCount);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -315,6 +315,30 @@ private Q_SLOTS:
renderQueue->reset();
// WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityHierarchyDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // EntityEnabledDirty
+ 1 + // EntityHierarchyJob
+ 1 + // WorldTransformJob
+ 1 + // UpdateWorldBoundingVolume
+ 1 + // UpdateShaderDataTransform
+ 1 + // ExpandBoundingVolumeJob
+ 1 + // UpdateEntityLayersJob
+ 1 + // updateLevelOfDetailJob
+ 1 + // syncLoadingJobs
+ 1 + // updateSkinningPaletteJob
+ 1 + // cleanupJob
+ 1 + // sendBufferCaptureJob
+ singleRenderViewJobCount +
+ layerCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr);
jobs = renderer.renderBinJobs();
@@ -322,6 +346,7 @@ private Q_SLOTS:
// and ShaderGathererJob are not added here)
QCOMPARE(jobs.size(),
1 + // EntityEnabledDirty
+ 1 + // EntityHierarchyDirty
1 + // WorldTransformJob
1 + // UpdateWorldBoundingVolume
1 + // UpdateShaderDataTransform
@@ -335,13 +360,13 @@ private Q_SLOTS:
1 + // VAOGatherer
1 + // BufferGathererJob
1 + // TexturesGathererJob
+ 1 + // UpdateEntityLayersJob
1 + // LightGathererJob
1 + // CacheLightJob
1 + // RenderableEntityFilterJob
1 + // CacheRenderableEntitiesJob
1 + // ComputableEntityFilterJob
1 + // CacheComputableEntitiesJob
- 1 + // UpdateEntityLayersJob
singleRenderViewJobCount +
layerCacheJobCount +
renderViewBuilderMaterialCacheJobCount);
diff --git a/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp b/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
index 007bfac7c..a7c81ca20 100644
--- a/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
+++ b/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
@@ -474,6 +474,7 @@ private slots:
void shouldHandleShaderCodeGeneration()
{
+ QSKIP("Disabled for Qt Base QShaderGenerator Integration");
// GIVEN
Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json");
QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty());
@@ -562,6 +563,8 @@ private slots:
void checkCodeUpdatedNotification()
{
// GIVEN
+ QSKIP("Disabled for Qt Base QShaderGenerator Integration");
+
Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json");
QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty());
QFETCH(Qt3DRender::Render::ShaderBuilder::ShaderType, type);
diff --git a/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp b/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp
index 67ddccd9b..4bab46423 100644
--- a/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp
+++ b/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QTest>
#include <Qt3DRender/private/updateshaderdatatransformjob_p.h>
#include <Qt3DRender/private/updateworldtransformjob_p.h>
+#include <Qt3DRender/private/updateentityhierarchyjob_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DRender/private/managers_p.h>
#include <Qt3DRender/qrenderaspect.h>
@@ -88,6 +89,10 @@ namespace {
void runRequiredJobs(Qt3DRender::TestAspect *test)
{
+ Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob;
+ updateEntitiesJob.setManager(test->nodeManagers());
+ updateEntitiesJob.run();
+
Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
updateWorldTransform.setRoot(test->sceneRoot());
updateWorldTransform.run();
diff --git a/tests/manual/cylinder-parent-test/cylinder-parent-test.pro b/tests/manual/cylinder-parent-test/cylinder-parent-test.pro
new file mode 100644
index 000000000..d3db3bc76
--- /dev/null
+++ b/tests/manual/cylinder-parent-test/cylinder-parent-test.pro
@@ -0,0 +1,9 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += 3dcore 3drender 3dinput 3dextras
+
+SOURCES += main.cpp
+
+
diff --git a/tests/manual/cylinder-parent-test/main.cpp b/tests/manual/cylinder-parent-test/main.cpp
new file mode 100644
index 000000000..823461a28
--- /dev/null
+++ b/tests/manual/cylinder-parent-test/main.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QTimer>
+
+#include <Qt3DInput/QInputAspect>
+
+#include <Qt3DRender/qcamera.h>
+#include <Qt3DRender/qcameralens.h>
+#include <Qt3DExtras/qcylindermesh.h>
+#include <Qt3DRender/qmesh.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DExtras/qphongmaterial.h>
+#include <Qt3DRender/qeffect.h>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DExtras/qforwardrenderer.h>
+
+#include <Qt3DCore/qentity.h>
+#include <Qt3DCore/qtransform.h>
+#include <Qt3DCore/qaspectengine.h>
+
+#include <Qt3DExtras/qt3dwindow.h>
+#include <Qt3DExtras/qorbitcameracontroller.h>
+#include <QThread>
+#include <QLoggingCategory>
+
+#include <type_traits>
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+ Qt3DExtras::Qt3DWindow view;
+
+ QLoggingCategory::setFilterRules("Qt3D.Renderer.RenderNodes=true");
+
+ // Root entity
+ Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
+ view.setRootEntity(rootEntity);
+ rootEntity->setObjectName("Root Entity");
+
+ // Set root object of the scene
+ view.show();
+
+ // Camera
+ Qt3DRender::QCamera *camera = view.camera();
+ camera->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f);
+ camera->setPosition(QVector3D(0, 0, 20.0f));
+ camera->setUpVector(QVector3D(0, 1, 0));
+ camera->setViewCenter(QVector3D(0, 0, 0));
+
+ // For camera controls
+ Qt3DExtras::QOrbitCameraController *cameraController = new Qt3DExtras::QOrbitCameraController(rootEntity);
+ cameraController->setCamera(camera);
+
+ // Cylinder shape data
+ Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh();
+
+ qDebug() << "Setup complete. Creating cylinders\n";
+
+ // simple setParent from nullptr (OK for QTBUG-73905)
+ // green cylinder, bottom left
+ {
+ Qt3DCore::QTransform *leftTransform = new Qt3DCore::QTransform;
+ leftTransform->setTranslation(QVector3D(-5, -2, 0));
+ leftTransform->setObjectName("Green transform");
+
+ Qt3DExtras::QPhongMaterial *greenMaterial = new Qt3DExtras::QPhongMaterial(rootEntity);
+ greenMaterial->setObjectName("Green Material");
+ greenMaterial->setDiffuse(Qt::green);
+
+ Qt3DCore::QEntity *grandParentNode = new Qt3DCore::QEntity();
+ Qt3DCore::QEntity *parentNode = new Qt3DCore::QEntity();
+ Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity();
+ grandParentNode->setObjectName("Green Grandparent");
+ parentNode->setObjectName("Green Parent");
+ leafNode->setObjectName("Green Leaf");
+
+ leafNode->addComponent(mesh);
+ leafNode->addComponent(greenMaterial);
+ parentNode->addComponent(leftTransform);
+
+ grandParentNode->setParent(rootEntity);
+ parentNode->setParent(grandParentNode);
+ leafNode->setParent(parentNode);
+ }
+
+ // simple setParent from rootEntity (doesn't work QTBUG-73905)
+ // yellow cylinder, top left
+ {
+ Qt3DCore::QTransform *leftTransform = new Qt3DCore::QTransform;
+ leftTransform->setTranslation(QVector3D(-5, 2, 0));
+ leftTransform->setObjectName("Yellow Transform");
+
+ Qt3DExtras::QPhongMaterial *yellowMaterial = new Qt3DExtras::QPhongMaterial(rootEntity);
+ yellowMaterial->setObjectName("Yellow Material");
+ yellowMaterial->setDiffuse(Qt::yellow);
+
+ Qt3DCore::QEntity *grandParentNode = new Qt3DCore::QEntity(rootEntity);
+ Qt3DCore::QEntity *parentNode = new Qt3DCore::QEntity(rootEntity);
+ Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(rootEntity);
+ leafNode->setObjectName("Yellow Leaf");
+ grandParentNode->setObjectName("Yellow Grandparent");
+ parentNode->setObjectName("Yellow Parent");
+
+ leafNode->addComponent(mesh);
+ leafNode->addComponent(yellowMaterial);
+ parentNode->addComponent(leftTransform);
+
+ // sometimes this can change things
+ //QCoreApplication::processEvents();
+
+ grandParentNode->setParent(rootEntity);
+ parentNode->setParent(grandParentNode);
+ leafNode->setParent(parentNode);
+ }
+
+ // complex setParent from nullptr (OK QTBUG-73905?)
+ // red cylinder, Bottom-right
+ {
+ Qt3DCore::QNode *tree1node1 = new Qt3DCore::QNode();
+ Qt3DCore::QEntity *tree1node2 = new Qt3DCore::QEntity();
+ Qt3DCore::QNode *tree1node3 = new Qt3DCore::QNode();
+ tree1node1->setObjectName("Red Tree1-Node1");
+ tree1node2->setObjectName("Red Tree1-Node2");
+ tree1node3->setObjectName("Red Tree1-Node3");
+
+ Qt3DCore::QNode *tree2node1 = new Qt3DCore::QNode();
+ Qt3DCore::QEntity *tree2node2 = new Qt3DCore::QEntity();
+ Qt3DCore::QNode *tree2node3 = new Qt3DCore::QNode();
+ tree2node1->setObjectName("Red Tree2-Node1");
+ tree2node2->setObjectName("Red Tree2-Node2");
+ tree2node3->setObjectName("Red Tree2-Node3");
+
+ Qt3DCore::QTransform *wrongRedTransform = new Qt3DCore::QTransform;
+ wrongRedTransform->setTranslation(QVector3D(1, -1, 0));
+ Qt3DCore::QTransform *bottomRightTransform = new Qt3DCore::QTransform;
+ bottomRightTransform->setTranslation(QVector3D(5, -2, 0));
+ bottomRightTransform->setObjectName("Red BR Transform");
+ wrongRedTransform->setObjectName("Red Wrong Transform");
+
+ Qt3DExtras::QPhongMaterial *redMaterial = new Qt3DExtras::QPhongMaterial(rootEntity);
+ redMaterial->setDiffuse(Qt::red);
+ redMaterial->setObjectName("Red Material");
+ Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity();
+ leafNode->setObjectName("Red Leaf");
+ leafNode->addComponent(mesh);
+ leafNode->addComponent(redMaterial);
+
+ tree1node2->addComponent(wrongRedTransform);
+ tree2node2->addComponent(bottomRightTransform);
+
+ tree1node1->setParent(rootEntity);
+ tree1node2->setParent(tree1node1);
+ tree1node3->setParent(tree1node2);
+
+ tree2node1->setParent(rootEntity);
+ tree2node2->setParent(tree2node1);
+ tree2node3->setParent(tree2node2);
+
+ leafNode->setParent(tree1node3);
+ leafNode->setParent(tree2node3);
+ }
+
+ // complex setParent from rootEntity (doesn't work QTBUG-73905)
+ // blue cylinder, top right
+ {
+ Qt3DCore::QNode *tree1node1 = new Qt3DCore::QNode(rootEntity);
+ Qt3DCore::QEntity *tree1node2 = new Qt3DCore::QEntity(rootEntity);
+ Qt3DCore::QNode *tree1node3 = new Qt3DCore::QNode(rootEntity);
+ tree1node1->setObjectName("Blue Tree1-Node1");
+ tree1node2->setObjectName("Blue Tree1-Node2");
+ tree1node3->setObjectName("Blue Tree1-Node3");
+
+ Qt3DCore::QNode *tree2node1 = new Qt3DCore::QNode(rootEntity);
+ Qt3DCore::QEntity *tree2node2 = new Qt3DCore::QEntity(rootEntity);
+ Qt3DCore::QNode *tree2node3 = new Qt3DCore::QNode(rootEntity);
+ tree2node1->setObjectName("Blue Tree2-Node1");
+ tree2node2->setObjectName("Blue Tree2-Node2");
+ tree2node3->setObjectName("Blue Tree2-Node3");
+
+ Qt3DCore::QTransform *wrongBlueTransform = new Qt3DCore::QTransform;
+ wrongBlueTransform->setTranslation(QVector3D(1, 1, 0));
+ Qt3DCore::QTransform *topRightTransform = new Qt3DCore::QTransform;
+ topRightTransform->setTranslation(QVector3D(5, 2, 0));
+ wrongBlueTransform->setObjectName("Blue Wrong Transform");
+ topRightTransform->setObjectName("Blue TR Transform");
+
+ Qt3DExtras::QPhongMaterial *blueMaterial = new Qt3DExtras::QPhongMaterial(rootEntity);
+ blueMaterial->setObjectName("Blue Material");
+ blueMaterial->setDiffuse(Qt::blue);
+ Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(rootEntity);
+ leafNode->addComponent(mesh);
+ leafNode->addComponent(blueMaterial);
+ leafNode->setObjectName("Blue Leaf");
+
+ // sometimes this can change things
+ //QCoreApplication::processEvents();
+
+ tree1node2->addComponent(wrongBlueTransform);
+ tree2node2->addComponent(topRightTransform);
+
+ tree1node1->setParent(rootEntity);
+ tree1node2->setParent(tree1node1);
+ tree1node3->setParent(tree1node2);
+
+ tree2node1->setParent(rootEntity);
+ tree2node2->setParent(tree2node1);
+ tree2node3->setParent(tree2node2);
+
+ leafNode->setParent(tree1node3);
+ leafNode->setParent(tree2node3);
+ }
+
+ return app.exec();
+}
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index f5a51907a..93890007c 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -13,6 +13,7 @@ SUBDIRS += \
custom-mesh-update-data-cpp \
custom-mesh-update-data-qml \
cylinder-cpp \
+ cylinder-parent-test \
cylinder-qml \
deferred-renderer-cpp \
deferred-renderer-qml \
diff --git a/tests/tests.pro b/tests/tests.pro
index 157bdcea5..17c91cb1f 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -1,5 +1,8 @@
TEMPLATE = subdirs
+!qtHaveModule(3dcore): \
+ return()
+
QT_FOR_CONFIG += 3dcore
!package {
diff --git a/tools/tools.pro b/tools/tools.pro
index 121507613..3b457ed50 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -1,4 +1,8 @@
TEMPLATE = subdirs
+
+!qtHaveModule(3dcore): \
+ return()
+
QT_FOR_CONFIG += 3dcore-private
qtConfig(assimp):qtConfig(commandlineparser): {
SUBDIRS += qgltf