summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorSean Harmer <sean.harmer@kdab.com>2017-08-06 18:58:46 +0100
committerSean Harmer <sean.harmer@kdab.com>2017-08-16 13:19:23 +0000
commit5c6634f2d1cd9a016142c3641ab6d797f5fe4ba7 (patch)
treeccbad78b2839b26b1f10cf5aa112786df56bd018 /tests
parentb236f982170779a1836f7d391428287921422163 (diff)
Update skinned mesh example to expose joints of object
To do this we add a helper Sqt struct that wraps up an affine transformation as a scale vector, rotation quaternion and a translation vector. This is the format in which the animation aspect will animate the joints later so it's easier to keep the transforms split like this. It's also less data to move around compared with a 4x4 matrix (10 vs 16 floats, 12 including the padding). Change-Id: Iaa30b5ef5d1635cc208ead918827140cf2765908 Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/core/qskeletonloader/tst_qskeletonloader.cpp77
-rw-r--r--tests/auto/render/skeleton/tst_skeleton.cpp170
-rw-r--r--tests/manual/skinned-mesh/SkinnedEntity.qml2
-rw-r--r--tests/manual/skinned-mesh/main.qml1
4 files changed, 249 insertions, 1 deletions
diff --git a/tests/auto/core/qskeletonloader/tst_qskeletonloader.cpp b/tests/auto/core/qskeletonloader/tst_qskeletonloader.cpp
index db643fc43..1386429d4 100644
--- a/tests/auto/core/qskeletonloader/tst_qskeletonloader.cpp
+++ b/tests/auto/core/qskeletonloader/tst_qskeletonloader.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QTest>
#include <Qt3DCore/qskeletonloader.h>
+#include <Qt3DCore/qjoint.h>
#include <Qt3DCore/private/qskeletonloader_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qnodecreatedchange.h>
@@ -52,6 +53,7 @@ private Q_SLOTS:
// THEN
QCOMPARE(skeleton.source(), QUrl());
QCOMPARE(skeleton.status(), QSkeletonLoader::NotReady);
+ QCOMPARE(skeleton.isCreateJointsEnabled(), false);
}
void checkPropertyChanges()
@@ -78,6 +80,26 @@ private Q_SLOTS:
QCOMPARE(skeleton.source(), newValue);
QCOMPARE(spy.count(), 0);
}
+
+ {
+ // WHEN
+ QSignalSpy spy(&skeleton, SIGNAL(createJointsEnabledChanged(bool)));
+ const bool newValue(true);
+ skeleton.setCreateJointsEnabled(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(skeleton.isCreateJointsEnabled(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ skeleton.setCreateJointsEnabled(newValue);
+
+ // THEN
+ QCOMPARE(skeleton.isCreateJointsEnabled(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
}
void checkCreationData()
@@ -107,6 +129,7 @@ private Q_SLOTS:
QCOMPARE(skeleton.isEnabled(), creationChangeData->isNodeEnabled());
QCOMPARE(skeleton.metaObject(), creationChangeData->metaObject());
QCOMPARE(skeleton.source(), data.source);
+ QCOMPARE(skeleton.isCreateJointsEnabled(), data.createJoints);
}
// WHEN
@@ -122,15 +145,18 @@ private Q_SLOTS:
QCOMPARE(creationChanges.size(), 1);
const auto creationChangeData = qSharedPointerCast<QNodeCreatedChange<QSkeletonLoaderData>>(creationChanges.first());
+ const QSkeletonLoaderData data = creationChangeData->data;
QCOMPARE(skeleton.id(), creationChangeData->subjectId());
QCOMPARE(skeleton.isEnabled(), false);
QCOMPARE(skeleton.isEnabled(), creationChangeData->isNodeEnabled());
QCOMPARE(skeleton.metaObject(), creationChangeData->metaObject());
+ QCOMPARE(skeleton.source(), data.source);
+ QCOMPARE(skeleton.isCreateJointsEnabled(), data.createJoints);
}
}
- void checkSourceUpdate()
+ void checkPropertyUpdates()
{
// GIVEN
TestArbiter arbiter;
@@ -160,6 +186,29 @@ private Q_SLOTS:
QCOMPARE(arbiter.events.size(), 0);
}
+
+ {
+ // WHEN
+ skeleton.setCreateJointsEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "createJointsEnabled");
+ QCOMPARE(change->type(), PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ skeleton.setCreateJointsEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
}
void checkStatusPropertyUpdate()
@@ -194,6 +243,32 @@ private Q_SLOTS:
QCOMPARE(arbiter.events.size(), 0);
QCOMPARE(status(), newStatus);
}
+
+ void checkRootJointPropertyUpdate()
+ {
+ // GIVEN
+ qRegisterMetaType<Qt3DCore::QJoint*>();
+ TestArbiter arbiter;
+ arbiter.setArbiterOnNode(this);
+ QSignalSpy spy(this, SIGNAL(rootJointChanged(Qt3DCore::QJoint*)));
+ std::unique_ptr<QJoint> root(new QJoint());
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QVERIFY(rootJoint() == nullptr);
+
+ // WHEN
+ auto valueChange = QJointChangePtr::create(id());
+ valueChange->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ valueChange->setPropertyName("rootJoint");
+ valueChange->data = std::move(root);
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 1);
+ QVERIFY(rootJoint() != nullptr);
+ }
};
QTEST_MAIN(tst_QSkeletonLoader)
diff --git a/tests/auto/render/skeleton/tst_skeleton.cpp b/tests/auto/render/skeleton/tst_skeleton.cpp
index 072b90c2f..64359fc78 100644
--- a/tests/auto/render/skeleton/tst_skeleton.cpp
+++ b/tests/auto/render/skeleton/tst_skeleton.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QTest>
#include <Qt3DRender/private/skeleton_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DCore/qjoint.h>
#include <Qt3DCore/qskeletonloader.h>
#include <Qt3DCore/private/qnode_p.h>
#include <Qt3DCore/private/qscene_p.h>
@@ -43,6 +44,27 @@ using namespace Qt3DCore;
using namespace Qt3DRender;
using namespace Qt3DRender::Render;
+Q_DECLARE_METATYPE(Qt3DRender::Render::JointInfo)
+Q_DECLARE_METATYPE(Qt3DRender::Render::SkeletonData)
+
+namespace {
+
+void linearizeTreeHelper(QJoint *joint, QVector<QJoint *> &joints)
+{
+ joints.push_back(joint);
+ for (const auto child : joint->childJoints())
+ linearizeTreeHelper(child, joints);
+}
+
+QVector<QJoint *> linearizeTree(QJoint *rootJoint)
+{
+ QVector<QJoint *> joints;
+ linearizeTreeHelper(rootJoint, joints);
+ return joints;
+}
+
+}
+
class tst_Skeleton : public Qt3DCore::QBackendNodeTester
{
Q_OBJECT
@@ -169,6 +191,154 @@ private Q_SLOTS:
arbiter.events.clear();
}
+
+ void checkCreateFrontendJoint_data()
+ {
+ QTest::addColumn<JointInfo>("jointInfo");
+ QTest::addColumn<QJoint *>("expectedJoint");
+
+ QTest::newRow("default") << JointInfo() << new QJoint();
+
+ const QVector3D t(1.0f, 2.0f, 3.0f);
+ const QQuaternion r = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 45.0f);
+ const QVector3D s(1.5f, 2.5f, 3.5f);
+ JointInfo jointInfo;
+ jointInfo.localPose.scale = s;
+ jointInfo.localPose.rotation = r;
+ jointInfo.localPose.translation = t;
+
+ QJoint *joint = new QJoint();
+ joint->setTranslation(t);
+ joint->setRotation(r);
+ joint->setScale(s);
+ QTest::newRow("localPose") << jointInfo << joint;
+
+ QMatrix4x4 m;
+ m.rotate(r);
+ m.scale(QVector3D(1.0f, 1.0f, 1.0f) / s);
+ m.translate(-t);
+ jointInfo.inverseBindPose = m;
+
+ joint = new QJoint();
+ joint->setTranslation(t);
+ joint->setRotation(r);
+ joint->setScale(s);
+ joint->setInverseBindMatrix(m);
+ QTest::newRow("inverseBind") << jointInfo << joint;
+ }
+
+ void checkCreateFrontendJoint()
+ {
+ // GIVEN
+ Skeleton backendSkeleton;
+ QFETCH(JointInfo, jointInfo);
+ QFETCH(QJoint *, expectedJoint);
+
+ // WHEN
+ const QJoint *actualJoint = backendSkeleton.createFrontendJoint(jointInfo);
+
+ // THEN
+ QCOMPARE(actualJoint->scale(), expectedJoint->scale());
+ QCOMPARE(actualJoint->rotation(), expectedJoint->rotation());
+ QCOMPARE(actualJoint->translation(), expectedJoint->translation());
+ QCOMPARE(actualJoint->inverseBindMatrix(), expectedJoint->inverseBindMatrix());
+
+ // Cleanup
+ delete actualJoint;
+ delete expectedJoint;
+ }
+
+ void checkCreateFrontendJoints_data()
+ {
+ QTest::addColumn<SkeletonData>("skeletonData");
+ QTest::addColumn<QJoint *>("expectedRootJoint");
+
+ QTest::newRow("empty") << SkeletonData() << (QJoint*)nullptr;
+
+ SkeletonData skeletonData;
+ JointInfo rootJointInfo;
+ skeletonData.joints.push_back(rootJointInfo);
+ const int childCount = 10;
+ for (int i = 0; i < childCount; ++i) {
+ JointInfo childJointInfo;
+ const float x = static_cast<float>(i);
+ childJointInfo.localPose.translation = QVector3D(x, x, x);
+ childJointInfo.parentIndex = 0;
+ skeletonData.joints.push_back(childJointInfo);
+ }
+
+ QJoint *rootJoint = new QJoint();
+ for (int i = 0; i < childCount; ++i) {
+ QJoint *childJoint = new QJoint();
+ const float x = static_cast<float>(i);
+ childJoint->setTranslation(QVector3D(x, x, x));
+ rootJoint->addChildJoint(childJoint);
+ }
+
+ QTest::newRow("wide") << skeletonData << rootJoint;
+
+ skeletonData.joints.clear();
+ skeletonData.joints.push_back(rootJointInfo);
+ for (int i = 0; i < childCount; ++i) {
+ JointInfo childJointInfo;
+ const float x = static_cast<float>(i);
+ childJointInfo.localPose.translation = QVector3D(x, x, x);
+ childJointInfo.parentIndex = i;
+ skeletonData.joints.push_back(childJointInfo);
+ }
+
+ rootJoint = new QJoint();
+ QJoint *previousJoint = rootJoint;
+ for (int i = 0; i < childCount; ++i) {
+ QJoint *childJoint = new QJoint();
+ const float x = static_cast<float>(i);
+ childJoint->setTranslation(QVector3D(x, x, x));
+ previousJoint->addChildJoint(childJoint);
+ previousJoint = childJoint;
+ }
+
+ QTest::newRow("deep") << skeletonData << rootJoint;
+ }
+
+ void checkCreateFrontendJoints()
+ {
+ // GIVEN
+ Skeleton backendSkeleton;
+ QFETCH(SkeletonData, skeletonData);
+ QFETCH(QJoint *, expectedRootJoint);
+
+ // WHEN
+ QJoint *actualRootJoint = backendSkeleton.createFrontendJoints(skeletonData);
+
+ // THEN
+ if (skeletonData.joints.isEmpty()) {
+ QVERIFY(actualRootJoint == expectedRootJoint); // nullptr
+ return;
+ }
+
+ // Linearise the tree of joints and check them against the skeletonData
+ QVector<QJoint *> joints = linearizeTree(actualRootJoint);
+ QCOMPARE(joints.size(), skeletonData.joints.size());
+ for (int i = 0; i < joints.size(); ++i) {
+ // Check the translations match
+ QCOMPARE(joints[i]->translation(), skeletonData.joints[i].localPose.translation);
+ }
+
+ // Now we know the order of Joints match. Check the parents match too
+ for (int i = 0; i < joints.size(); ++i) {
+ // Get parent index from joint info
+ const int parentIndex = skeletonData.joints[i].parentIndex;
+ if (parentIndex == -1) {
+ QVERIFY(joints[i]->parent() == nullptr);
+ } else {
+ QCOMPARE(joints[i]->parent(), joints[parentIndex]);
+ }
+ }
+
+ // Cleanup
+ delete actualRootJoint;
+ delete expectedRootJoint;
+ }
};
QTEST_APPLESS_MAIN(tst_Skeleton)
diff --git a/tests/manual/skinned-mesh/SkinnedEntity.qml b/tests/manual/skinned-mesh/SkinnedEntity.qml
index 455f3869e..3abafa209 100644
--- a/tests/manual/skinned-mesh/SkinnedEntity.qml
+++ b/tests/manual/skinned-mesh/SkinnedEntity.qml
@@ -8,6 +8,7 @@ Entity {
property Effect effect: skinnedPbrEffect
property url source: ""
+ property alias createJointsEnabled: skeleton.createJointsEnabled
property alias transform: transform
property color baseColor: "red"
@@ -21,6 +22,7 @@ Entity {
},
Armature {
skeleton: SkeletonLoader {
+ id: skeleton
source: root.source
onStatusChanged: console.log("skeleton loader status: " + status)
onJointCountChanged: console.log("skeleton has " + jointCount + " joints")
diff --git a/tests/manual/skinned-mesh/main.qml b/tests/manual/skinned-mesh/main.qml
index 31c618382..a3d8e3d12 100644
--- a/tests/manual/skinned-mesh/main.qml
+++ b/tests/manual/skinned-mesh/main.qml
@@ -73,5 +73,6 @@ DefaultSceneEntity {
baseColor: "blue"
transform.scale: 0.05
transform.translation: Qt.vector3d(0.5, 0.25, 0.0)
+ createJointsEnabled: true
}
}