Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-67951

Qt3D QSkeletonLoader fatal error Q_ASSERT(jointIndex != -1)

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P1: Critical P1: Critical
    • None
    • 5.10.1
    • Qt3D
    • 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);
          }
      }
      
      

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            seanharmer Sean Harmer
            zinger Dmitry Felzinger
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved:

                There are no open Gerrit changes