-
Bug
-
Resolution: Cannot Reproduce
-
P1: Critical
-
None
-
5.10.1
-
None
-
*Windows 10 Qt 5.10.1*
All code is written in c++ without using QML.
Consider the code below:
addComponent(m_transform); m_mesh - >setSource(source()); addComponent(m_mesh); m_skeletonLoader->setCreateJointsEnabled(false); //Problematic line m_skeletonLoader->setSource(source()); m_armature - >setSkeleton(m_skeletonLoader); addComponent(m_armature);
If сreateJointsEnabled set false. Booting occurs without errors.
But since the project requires access to the bones and joints. Requires sreatejointsenabled set true.
As a result, a critical error occurs in the Q_ASSERT(jointIndex!) file= -1); render\geometry\skeleton.cpp line 388.
See below for the function code:
// 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; }
This function is called in the updateskinningpalettejob file.cpp, the following shows the location of the call
void UpdateSkinningPaletteJob::run() { // TODO: Decide this job across several jobs, say one per skeleton so // that it can be done in parallel // Update the local pose transforms of JointInfo'from s in Skeletons // the set of dirty joints. for (const auto &jointHandle : qAsConst(m_dirtyJoints)) { Joint *joint = m_nodeManagers - >jointManager () - >data(jointHandle); Q_ASSERT(joint); Skeleton *skeleton = m_nodeManagers - >skeletonManager () - >data(joint - >owningSkeleton()); Q_ASSERT(skeleton); if (skeleton - >isEnabled) & & & joint - >isEnabled()) skeleton->setLocalPose(jointHandle, joint->localPose()); //call the problematic function } // Find all the armature components and update their skinning palettes QVector<HArmature> dirtyArmatures; findDirtyArmatures(m_root, dirtyArmatures); // Update the skeleton for each dirty armature auto armatureManager = m_nodeManagers - >armatureManager(); auto skeletonManager = m_nodeManagers - >skeletonManager(); for (const auto &armatureHandle : qAsConst(dirtyArmatures)) { auto armature = armatureManager - >data(armatureHandle); Q_ASSERT(armature); auto skeletonId = armature - >skeletonId(); auto skeleton = skeletonManager - >lookupResource(skeletonId); Q_ASSERT(skeleton); const QVector<QMatrix4x4> skinningPalette = skeleton - >calculateSkinningMatrixPalette(); armature - >skinningPaletteUniform ().setData(skinningPalette); } }
The error occurs because the loadSkeleton() function is from the render\geometry\skeleton file.cpp is called twice.
The first time to download from a file, the function is presented below:
void Skeleton:: loadSkeletonFromUrl() { // TODO: Handle remote files QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_source); QFileInfo info(filePath); if (!info.exists()) { qWarning () < < "Could not open skeleton file:" < < filePath; setStatus(Qt3DCore:: QSkeletonLoader::Error); return; } QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qWarning () < < "Could not open skeleton file:" < < filePath; setStatus(Qt3DCore:: QSkeletonLoader::Error); return; } // TODO: Make plugin based for more file type support. For now gltf or native const QString ext = info.suffix(); if (ext == QLatin1String ("gltf")) { GLTFSkeletonLoader loader; loader.load(&file); m_skeletonData = loader.createSkeleton(m_name); // If the user has requested it, create the frontend nodes for the joints // and send them to the (soon to be owning) QSkeletonLoader. if (m_createJoints) { std::unique_ptr<QJoint> rootJoint(createFrontendJoints(m_skeletonData)); if (!rootJoint) { qWarning() << "Failed to create the frontend joints"; setStatus(Qt3DCore:: QSkeletonLoader::Error); return; } // Move the QJoint tree to the main thread and notify the // corresponding QSkeletonLoader const auto appThread = QCoreApplication::instance () - >thread(); rootJoint - >moveToThread(appThread); auto e = QJointChangePtr:: create(peerId()); e - >setDeliveryFlags(Qt3DCore:: QSceneChange::Nodes); e - >setPropertyName ("rootJoint"); e - >data = std:: move(rootJoint); by the notifyobservers(e); // Clear the skeleton data. It will be recreated from the // frontend joints. A little bit ineffective but ensues // that joints created this way and via QSkeleton go through // the same code path. m_skeletonData = SkeletonData(); } } else if (ext == QLatin1String ("json")) { // TODO: Support native skeleton type } else { qWarning() << "Unknown file type skeleton:" << ext; setStatus(Qt3DCore:: QSkeletonLoader::Error); return; } m_skinningPalette.resize (m_skeletondata.joints.size()); }
After a successful download of the file is populated structure m_skeletonData, then flag is checked m_createJoints,
if it is equal to true, the process of building joints and bones starts, and the structure
m_skeletondata is reset.
Then download from the received data. In parallel, the start function UpdateSkinningPaletteJob::run().,
because the download from the file completed successfully.
As a result, the time when m_dirtyJoints.size ()!= m_skeletonData.jointIndices.size ().
And when calling Skeleton:: setLocalPose (HJoint jointHandle, const Qt3DCore::Sqt &localPose) occurs
a critical error in the file Q_ASSERT(jointIndex != -1); render\geometry\skeleton.cpp line 388.
The fastest solution is to modify the function UpdateSkinningPaletteJob::run(). The code is presented below:
void UpdateSkinningPaletteJob::run() { // TODO: Decide this job across several jobs, say one per skeleton so // that it can be done in parallel // Update the local pose transforms of JointInfo'from s in Skeletons // the set of dirty joints. for (const auto &jointHandle : qAsConst(m_dirtyJoints)) { Joint *joint = m_nodeManagers - >jointManager () - >data(jointHandle); Q_ASSERT(joint); Skeleton *skeleton = m_nodeManagers - >skeletonManager () - >data(joint - >owningSkeleton()); Q_ASSERT(skeleton); ///================ Modification==================== if (m_dirtyJoints.size ()!= skeleton - >jointCount()) { return; } ///================================================== if (skeleton - >isEnabled) & & & joint - >isEnabled()) skeleton - >setLocalPose(jointHandle, joint - >localPose()); } // Find all the armature components and update their skinning palettes QVector<HArmature> dirtyArmatures; findDirtyArmatures(m_root, dirtyArmatures); // Update the skeleton for each dirty armature auto armatureManager = m_nodeManagers - >armatureManager(); auto skeletonManager = m_nodeManagers - >skeletonManager(); for (const auto &armatureHandle : qAsConst(dirtyArmatures)) { auto armature = armatureManager - >data(armatureHandle); Q_ASSERT(armature); auto skeletonId = armature - >skeletonId(); auto skeleton = skeletonManager - >lookupResource(skeletonId); Q_ASSERT(skeleton); const QVector<QMatrix4x4> skinningPalette = skeleton - >calculateSkinningMatrixPalette(); armature - >skinningPaletteUniform ().setData(skinningPalette); } }
Весь код написан на c++ без использования QML.
Рассмотрим код ниже:
addComponent(m_transform); m_mesh->setSource(source()); addComponent(m_mesh); m_skeletonLoader->setCreateJointsEnabled(false); //Проблемная строка m_skeletonLoader->setSource(source()); m_armature->setSkeleton(m_skeletonLoader); addComponent(m_armature);
Если сreateJointsEnabled установить false. Загрузщка происходит без ошибок.
Но так как в проекте требуется получить доступ к костям и суставам. Требуется сreateJointsEnabled установить true.
В результате происходит критическая ошибка в файле Q_ASSERT(jointIndex != -1); render\geometry\skeleton.cpp строка 388.
Код функции смотри ниже:
// 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; }
Данная функция вызывается в файле updateskinningpalettejob.cpp, ниже приведено место вызова
void UpdateSkinningPaletteJob::run() { // TODO: Decompose this job across several jobs, say one per skeleton so // that it can be done in parallel // Update the local pose transforms of JointInfo's in Skeletons from // the set of dirty joints. for (const auto &jointHandle : qAsConst(m_dirtyJoints)) { Joint *joint = m_nodeManagers->jointManager()->data(jointHandle); Q_ASSERT(joint); Skeleton *skeleton = m_nodeManagers->skeletonManager()->data(joint->owningSkeleton()); Q_ASSERT(skeleton); if (skeleton->isEnabled() && joint->isEnabled()) skeleton->setLocalPose(jointHandle, joint->localPose()); //Место вызова проблемной функции } // Find all the armature components and update their skinning palettes QVector<HArmature> dirtyArmatures; findDirtyArmatures(m_root, dirtyArmatures); // Update the skeleton for each dirty armature auto armatureManager = m_nodeManagers->armatureManager(); auto skeletonManager = m_nodeManagers->skeletonManager(); for (const auto &armatureHandle : qAsConst(dirtyArmatures)) { auto armature = armatureManager->data(armatureHandle); Q_ASSERT(armature); auto skeletonId = armature->skeletonId(); auto skeleton = skeletonManager->lookupResource(skeletonId); Q_ASSERT(skeleton); const QVector<QMatrix4x4> skinningPalette = skeleton->calculateSkinningMatrixPalette(); armature->skinningPaletteUniform().setData(skinningPalette); } }
Ошибка возникает в результате того, функция loadSkeleton() из файла render\geometry\skeleton.cpp вызывается два раза.
Первый раз производиться загрузка из файла, функция представлена ниже:
void Skeleton::loadSkeletonFromUrl() { // TODO: Handle remote files QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_source); QFileInfo info(filePath); if (!info.exists()) { qWarning() << "Could not open skeleton file:" << filePath; setStatus(Qt3DCore::QSkeletonLoader::Error); return; } QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Could not open skeleton file:" << filePath; setStatus(Qt3DCore::QSkeletonLoader::Error); return; } // TODO: Make plugin based for more file type support. For now gltf or native const QString ext = info.suffix(); if (ext == QLatin1String("gltf")) { GLTFSkeletonLoader loader; loader.load(&file); m_skeletonData = loader.createSkeleton(m_name); // If the user has requested it, create the frontend nodes for the joints // and send them to the (soon to be owning) QSkeletonLoader. if (m_createJoints) { std::unique_ptr<QJoint> rootJoint(createFrontendJoints(m_skeletonData)); if (!rootJoint) { qWarning() << "Failed to create frontend joints"; setStatus(Qt3DCore::QSkeletonLoader::Error); return; } // Move the QJoint tree to the main thread and notify the // corresponding QSkeletonLoader const auto appThread = QCoreApplication::instance()->thread(); rootJoint->moveToThread(appThread); auto e = QJointChangePtr::create(peerId()); e->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes); e->setPropertyName("rootJoint"); e->data = std::move(rootJoint); notifyObservers(e); // Clear the skeleton data. It will be recreated from the // frontend joints. A little bit inefficient but ensures // that joints created this way and via QSkeleton go through // the same code path. m_skeletonData = SkeletonData(); } } else if (ext == QLatin1String("json")) { // TODO: Support native skeleton type } else { qWarning() << "Unknown skeleton file type:" << ext; setStatus(Qt3DCore::QSkeletonLoader::Error); return; } m_skinningPalette.resize(m_skeletonData.joints.size()); }
После удачной загрузки из файл заполняется структура m_skeletonData, затем проверяется флаг m_createJoints,
если он равен true, то запускается процесс построения суставов и костей, а структура m_skeletonData обнуляется.
Далее производиться загрузка из полученных данных. Параллельно запускается функция UpdateSkinningPaletteJob::run().,
так как загрузка из файла завершена успешно.
В результате возникает момент когда m_dirtyJoints.size() != m_skeletonData.jointIndices.size().
И при вызове Skeleton::setLocalPose(HJoint jointHandle, const Qt3DCore::Sqt &localPose) происходит
критическая ошибка в файле Q_ASSERT(jointIndex != -1); render\geometry\skeleton.cpp строка 388.
Быстроее решение проблемы заключается в модификации функции UpdateSkinningPaletteJob::run(). Код представлен ниже:
void UpdateSkinningPaletteJob::run() { // TODO: Decompose this job across several jobs, say one per skeleton so // that it can be done in parallel // Update the local pose transforms of JointInfo's in Skeletons from // the set of dirty joints. for (const auto &jointHandle : qAsConst(m_dirtyJoints)) { Joint *joint = m_nodeManagers->jointManager()->data(jointHandle); Q_ASSERT(joint); Skeleton *skeleton = m_nodeManagers->skeletonManager()->data(joint->owningSkeleton()); Q_ASSERT(skeleton); ///================ Доработка ==================== if (m_dirtyJoints.size() != skeleton->jointCount()) { return; } ///================================================== if (skeleton->isEnabled() && joint->isEnabled()) skeleton->setLocalPose(jointHandle, joint->localPose()); } // Find all the armature components and update their skinning palettes QVector<HArmature> dirtyArmatures; findDirtyArmatures(m_root, dirtyArmatures); // Update the skeleton for each dirty armature auto armatureManager = m_nodeManagers->armatureManager(); auto skeletonManager = m_nodeManagers->skeletonManager(); for (const auto &armatureHandle : qAsConst(dirtyArmatures)) { auto armature = armatureManager->data(armatureHandle); Q_ASSERT(armature); auto skeletonId = armature->skeletonId(); auto skeleton = skeletonManager->lookupResource(skeletonId); Q_ASSERT(skeleton); const QVector<QMatrix4x4> skinningPalette = skeleton->calculateSkinningMatrixPalette(); armature->skinningPaletteUniform().setData(skinningPalette); } }
- relates to
-
QTBUG-80310 QSkeletonLoader fatal Q_ASSERT(jointIndex != -1)
-
- Reported
-