Revision control

++ // Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "skeleton_p.h"
++
++ #include <QCoreApplication>
++ #include <QFile>
++ #include <QFileInfo>
++
++ #include <Qt3DCore/qjoint.h>
++ #include <Qt3DRender/private/abstractrenderer_p.h>
++ #include <Qt3DRender/private/managers_p.h>
++ #include <Qt3DRender/private/nodemanagers_p.h>
++ #include <Qt3DRender/private/renderlogging_p.h>
++ #include <Qt3DCore/private/qskeleton_p.h>
++ #include <Qt3DCore/private/qskeletonloader_p.h>
++ #include <Qt3DCore/private/qmath3d_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace Qt3DRender {
++ namespace Render {
++
++ using namespace Qt3DCore;
++
++ Skeleton::Skeleton()
++ : BackendNode(Qt3DCore::QBackendNode::ReadWrite)
++ , m_status(Qt3DCore::QSkeletonLoader::NotReady)
++ , m_createJoints(false)
++ , m_dataType(Unknown)
++ , m_skeletonManager(nullptr)
++ , m_jointManager(nullptr)
++ {
++ }
++
++ void Skeleton::cleanup()
++ {
++ m_source.clear();
++ m_status = Qt3DCore::QSkeletonLoader::NotReady;
++ m_createJoints = false;
++ m_rootJointId = Qt3DCore::QNodeId();
++ clearData();
++ setEnabled(false);
++ }
++
++ void Skeleton::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
++ {
++ BackendNode::syncFromFrontEnd(frontEnd, firstTime);
++ const QAbstractSkeleton *node = qobject_cast<const QAbstractSkeleton *>(frontEnd);
++ if (!node)
++ return;
++ const QSkeleton *skeletonNode = qobject_cast<const QSkeleton *>(frontEnd);
++ const QSkeletonLoader *loaderNode = qobject_cast<const QSkeletonLoader *>(frontEnd);
++
++ if (firstTime) {
++ m_skeletonHandle = m_skeletonManager->lookupHandle(peerId());
++
++ if (skeletonNode) {
++ m_dataType = Data;
++ m_rootJointId = skeletonNode->rootJoint()->id();
++ if (!m_rootJointId.isNull()) {
++ markDirty(AbstractRenderer::SkeletonDataDirty);
++ m_skeletonManager->addDirtySkeleton(SkeletonManager::SkeletonDataDirty, m_skeletonHandle);
++ }
++ }
++
++ if (loaderNode) {
++ m_dataType = File;
++ m_source = loaderNode->source();
++ if (!m_source.isEmpty()) {
++ markDirty(AbstractRenderer::SkeletonDataDirty);
++ m_skeletonManager->addDirtySkeleton(SkeletonManager::SkeletonDataDirty, m_skeletonHandle);
++ }
++ }
++ }
++
++ if (loaderNode) {
++ if (loaderNode->source() != m_source) {
++ m_source = loaderNode->source();
++ markDirty(AbstractRenderer::SkeletonDataDirty);
++ m_skeletonManager->addDirtySkeleton(SkeletonManager::SkeletonDataDirty, m_skeletonHandle);
++ }
++ m_createJoints = loaderNode->isCreateJointsEnabled();
++
++ auto newJointId = Qt3DCore::qIdForNode(loaderNode->rootJoint());
++ if (newJointId != m_rootJointId) {
++ m_rootJointId = newJointId;
++
++ // If using a QSkeletonLoader to create frontend QJoints, when those joints are
++ // set on the skeleton, we end up here. In order to allow the subsequent call
++ // to loadSkeleton(), see below, to build the internal data from the frontend
++ // joints rather than from the source url again, we need to change the data type
++ // to Data.
++ m_dataType = Data;
++
++ // If the joint changes, we need to rebuild our internal SkeletonData and
++ // the relationships between joints and skeleton. Mark the skeleton data as
++ // dirty so that we get a loadSkeletonJob executed to process this skeleton.
++ if (!m_rootJointId.isNull()) {
++ markDirty(AbstractRenderer::SkeletonDataDirty);
++ m_skeletonManager->addDirtySkeleton(SkeletonManager::SkeletonDataDirty, m_skeletonHandle);
++ }
++ }
++ }
++
++ auto d = Qt3DCore::QAbstractSkeletonPrivate::get(node);
++ m_skeletonData.localPoses = d->m_localPoses;
++ }
++
++ void Skeleton::setStatus(QSkeletonLoader::Status status)
++ {
++ if (status != m_status)
++ m_status = status;
++ }
++
++ void Skeleton::clearData()
++ {
++ m_name.clear();
++ m_skeletonData.joints.clear();
++ m_skeletonData.localPoses.clear();
++ m_skeletonData.jointNames.clear();
++ m_skeletonData.jointIndices.clear();
++ }
++
++ void Skeleton::setSkeletonData(const SkeletonData &data)
++ {
++ m_skeletonData = data;
++ m_skinningPalette.resize(m_skeletonData.joints.size());
++ }
++
++ // Called from UpdateSkinningPaletteJob
++ void Skeleton::setLocalPose(HJoint jointHandle, const Qt3DCore::Sqt &localPose)
++ {
++ // Find the corresponding index into the JointInfo vector
++ // and set the local pose
++ const int jointIndex = m_skeletonData.jointIndices.value(jointHandle, -1);
++ Q_ASSERT(jointIndex != -1);
++ m_skeletonData.localPoses[jointIndex] = localPose;
++ }
++
++ QVector<QMatrix4x4> Skeleton::calculateSkinningMatrixPalette()
++ {
++ const QVector<Sqt> &localPoses = m_skeletonData.localPoses;
++ QVector<JointInfo> &joints = m_skeletonData.joints;
++ for (int i = 0; i < m_skeletonData.joints.size(); ++i) {
++ // Calculate the global pose of this joint
++ JointInfo &joint = joints[i];
++ if (joint.parentIndex == -1) {
++ joint.globalPose = localPoses[i].toMatrix();
++ } else {
++ JointInfo &parentJoint = joints[joint.parentIndex];
++ joint.globalPose = parentJoint.globalPose * localPoses[i].toMatrix();
++ }
++
++ m_skinningPalette[i] = joint.globalPose * joint.inverseBindPose;
++ }
++ return m_skinningPalette;
++ }
++
++
++ SkeletonFunctor::SkeletonFunctor(AbstractRenderer *renderer,
++ SkeletonManager *skeletonManager,
++ JointManager *jointManager)
++ : m_renderer(renderer)
++ , m_skeletonManager(skeletonManager)
++ , m_jointManager(jointManager)
++ {
++ }
++
++ Qt3DCore::QBackendNode *SkeletonFunctor::create(Qt3DCore::QNodeId id) const
++ {
++ Skeleton *backend = m_skeletonManager->getOrCreateResource(id);
++ backend->setRenderer(m_renderer);
++ backend->setSkeletonManager(m_skeletonManager);
++ backend->setJointManager(m_jointManager);
++ return backend;
++ }
++
++ Qt3DCore::QBackendNode *SkeletonFunctor::get(Qt3DCore::QNodeId id) const
++ {
++ return m_skeletonManager->lookupResource(id);
++ }
++
++ void SkeletonFunctor::destroy(Qt3DCore::QNodeId id) const
++ {
++ m_skeletonManager->releaseResource(id);
++ }
++
++ } // namespace Render
++ } // namespace Qt3DRender
++
++ QT_END_NAMESPACE