code: private void makeSkeleton(GVRSceneObject root)
      {
          if (!mBoneMap.isEmpty())
          {
              BoneCollector nodeProcessor = new BoneCollector();
  
              root.forAllDescendants(nodeProcessor);
              mSkeleton = new GVRSkeleton(root, nodeProcessor.getBoneNames());
              GVRPose bindPose = new GVRPose(mSkeleton);
              Matrix4f bindPoseMtx = new Matrix4f();
              GVRSceneObject skelRoot = mSkeleton.getOwnerObject().getParent();
              Matrix4f rootMtx = skelRoot.getTransform().getModelMatrix4f();
  
              rootMtx.invert();
              for (int boneId = 0; boneId < mSkeleton.getNumBones(); ++boneId)
              {
                  String boneName = mSkeleton.getBoneName(boneId);
                  AiBone aiBone = mBoneMap.get(boneName);
                  GVRSceneObject bone = mSkeleton.getBone(boneId);
  
                  if (aiBone != null)
                  {
                      float[] matrixdata = aiBone.getOffsetMatrix(sWrapperProvider);
  
                      bindPoseMtx.set(matrixdata);
                      bindPoseMtx.invert();
                      bindPose.setWorldMatrix(boneId, bindPoseMtx);
                  }
                  else
                  {
                      GVRTransform t = bone.getTransform();
                      Matrix4f mtx = t.getModelMatrix4f();
  
                      mtx.invert();
                      rootMtx.mul(mtx, mtx);
                      bindPose.setWorldMatrix(boneId, mtx);
                      Log.e("BONE", "no bind pose matrix for bone %s", boneName);
                  }
              }
              mSkeleton.setBindPose(bindPose);
          }
      }
docs: /*
  if there was a skinned mesh the bone map acts as a lookup
  table that maps bone names to their corresponding AiBone objects.
  The BoneCollector constructs the skeleton from the bone names in
  the map and the scene nodes, attempting to connect bones
  where there are gaps to produce a complete skeleton.