Support RootTranslationOnly option for motion capture data 47/302247/1
authorseungho baek <sbsh.baek@samsung.com>
Tue, 21 Nov 2023 07:27:44 +0000 (16:27 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Mon, 4 Dec 2023 13:06:33 +0000 (22:06 +0900)
Change-Id: I4058c891336a6f802c6e70efe87e06ffdaabb1f5
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-BvhLoader.cpp
automated-tests/src/dali-scene3d/utc-Dali-MotionData.cpp
dali-scene3d/internal/model-motion/motion-data-impl.cpp
dali-scene3d/internal/model-motion/motion-data-impl.h
dali-scene3d/internal/model-motion/motion-data-load-task.cpp
dali-scene3d/internal/model-motion/motion-data-load-task.h
dali-scene3d/public-api/loader/bvh-loader.cpp
dali-scene3d/public-api/loader/bvh-loader.h
dali-scene3d/public-api/model-motion/motion-data.cpp
dali-scene3d/public-api/model-motion/motion-data.h

index 4fb5a79..b395804 100644 (file)
@@ -62,13 +62,13 @@ int UtcDaliLoadBvh(void)
     {
       case 0: // Load bvh from url
       {
-        animDef = LoadBvh(TEST_RESOURCE_DIR "/test.bvh", "testBvh");
+        animDef = LoadBvh(TEST_RESOURCE_DIR "/test.bvh", "testBvh", false);
         break;
       }
       case 1: // Load bvh from buffer stream.
       {
         std::string rawString = ReadBufferFromFile(TEST_RESOURCE_DIR "/test.bvh");
-        animDef               = LoadBvhFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()), "testBvh");
+        animDef               = LoadBvhFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()), "testBvh", false);
         break;
       }
     }
@@ -142,7 +142,7 @@ int UtcDaliLoadBvhFailed01(void)
 {
   TestApplication application;
 
-  AnimationDefinition animDef = LoadBvh("/nothing.bvh", "testBvh");
+  AnimationDefinition animDef = LoadBvh("/nothing.bvh", "testBvh", false);
   DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   END_TEST;
 }
@@ -151,7 +151,7 @@ int UtcDaliLoadBvhFailed02(void)
 {
   TestApplication application;
 
-  AnimationDefinition animDef = LoadBvhFromBuffer(nullptr, 0, "testBvh");
+  AnimationDefinition animDef = LoadBvhFromBuffer(nullptr, 0, "testBvh", false);
   DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   END_TEST;
 }
@@ -167,7 +167,7 @@ int UtcDaliLoadBvhFailed03(void)
     tet_printf("Parse error for hierarchy %u\n", tc);
     std::ostringstream oss;
     oss << TEST_RESOURCE_DIR << "/test-invalid-hierarchy" << tc << ".bvh";
-    AnimationDefinition animDef = LoadBvh(oss.str(), "testBvh");
+    AnimationDefinition animDef = LoadBvh(oss.str(), "testBvh", false);
     DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   }
 
@@ -177,13 +177,13 @@ int UtcDaliLoadBvhFailed03(void)
     tet_printf("Parse error for motion %u\n", tc);
     std::ostringstream oss;
     oss << TEST_RESOURCE_DIR << "/test-invalid-motion" << tc << ".bvh";
-    AnimationDefinition animDef = LoadBvh(oss.str(), "testBvh");
+    AnimationDefinition animDef = LoadBvh(oss.str(), "testBvh", false);
     DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   }
 
   {
     tet_infoline("empty file");
-    AnimationDefinition animDef = LoadBvh(TEST_RESOURCE_DIR "/test-empty.bvh", "testBvh");
+    AnimationDefinition animDef = LoadBvh(TEST_RESOURCE_DIR "/test-empty.bvh", "testBvh", false);
     DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   }
   END_TEST;
index daa558f..b8d593b 100644 (file)
@@ -403,4 +403,40 @@ int UtcDaliMotionDataLoadAsyncMultiple(void)
   DALI_TEST_EQUALS(gLoadCompleted, false, TEST_LOCATION);
 
   END_TEST;
+}
+
+int UtcDaliMotionDataLoadBvhUseRootTranslationOnly(void)
+{
+  ToolkitTestApplication application;
+
+  MotionData motionDataAllTranslation = MotionData::New();
+  motionDataAllTranslation.LoadBvh(TEST_BVH_FILE_NAME, false, Vector3::ONE, true);
+
+  DALI_TEST_EQUALS(motionDataAllTranslation.GetMotionCount(), 4, TEST_LOCATION);  
+
+  MotionData motionDataOnlyRootTranslation = MotionData::New();
+  motionDataOnlyRootTranslation.LoadBvh(TEST_BVH_FILE_NAME, true, Vector3::ONE, true);
+
+  DALI_TEST_EQUALS(motionDataOnlyRootTranslation.GetMotionCount(), 3, TEST_LOCATION);  
+
+  END_TEST;
+}
+
+int UtcDaliMotionDataLoadBvhFromBufferUseRootTranslationOnly(void)
+{
+  ToolkitTestApplication application;
+
+  std::string rawString = ReadBufferFromFile(TEST_BVH_FILE_NAME);
+
+  MotionData motionDataAllTranslation = MotionData::New();
+  motionDataAllTranslation.LoadBvhFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()), false, Vector3::ONE, true);
+
+  DALI_TEST_EQUALS(motionDataAllTranslation.GetMotionCount(), 4, TEST_LOCATION);  
+
+  MotionData motionDataOnlyRootTranslation = MotionData::New();
+  motionDataOnlyRootTranslation.LoadBvhFromBuffer(reinterpret_cast<uint8_t*>(rawString.data()), static_cast<int>(rawString.length()), true, Vector3::ONE, true);
+
+  DALI_TEST_EQUALS(motionDataOnlyRootTranslation.GetMotionCount(), 3, TEST_LOCATION);  
+
+  END_TEST;
 }
\ No newline at end of file
index bb3f1cc..f2f4b84 100644 (file)
@@ -117,17 +117,17 @@ float MotionData::GetDuration() const
   return mDurationSeconds;
 }
 
-void MotionData::LoadBvh(const std::string& path, const Vector3& scale, bool synchronousLoad)
+void MotionData::LoadBvh(const std::string& path, bool useRootTranslationOnly, const Vector3& scale, bool synchronousLoad)
 {
   CancelMotionDataLoad();
-  mMotionDataLoadTask = new MotionDataLoadTask(path, scale, MakeCallback(this, &MotionData::OnLoadCompleted));
+  mMotionDataLoadTask = new MotionDataLoadTask(path, useRootTranslationOnly, scale, MakeCallback(this, &MotionData::OnLoadCompleted));
   RequestMotionDataLoad(synchronousLoad);
 }
 
-void MotionData::LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale, bool synchronousLoad)
+void MotionData::LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale, bool synchronousLoad)
 {
   CancelMotionDataLoad();
-  mMotionDataLoadTask = new MotionDataLoadTask(rawBuffer, rawBufferLength, scale, MakeCallback(this, &MotionData::OnLoadCompleted));
+  mMotionDataLoadTask = new MotionDataLoadTask(rawBuffer, rawBufferLength, useRootTranslationOnly, scale, MakeCallback(this, &MotionData::OnLoadCompleted));
   RequestMotionDataLoad(synchronousLoad);
 }
 
index 7b5316a..02b0e7e 100644 (file)
@@ -106,12 +106,12 @@ public: // Public Method
   /**
    * @copydoc Dali::Scene3D::MotionData::LoadBvh()
    */
-  void LoadBvh(const std::string& path, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
+  void LoadBvh(const std::string& path, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
 
   /**
    * @copydoc Dali::Scene3D::MotionData::LoadBvhFromBuffer()
    */
-  void LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
+  void LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
 
   /**
    * @copydoc Dali::Scene3D::MotionData::LoadFacialAnimation()
index 7c6d95e..f780eb9 100644 (file)
@@ -31,23 +31,25 @@ namespace Scene3D
 {
 namespace Internal
 {
-MotionDataLoadTask::MotionDataLoadTask(const std::string& path, const Vector3& scale, CallbackBase* callback)
+MotionDataLoadTask::MotionDataLoadTask(const std::string& path, bool useRootTranslationOnly, const Vector3& scale, CallbackBase* callback)
 : AsyncTask(callback),
   mFileUrl(path),
   mRawBuffer(nullptr),
   mRawBufferLength(0),
   mScale(scale),
+  mUseRootTranslationOnly(useRootTranslationOnly),
   mAnimationDefinition{},
   mLoadMethod(MotionDataLoadTask::LoadMethod::BVH_FILE)
 {
 }
 
-MotionDataLoadTask::MotionDataLoadTask(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale, CallbackBase* callback)
+MotionDataLoadTask::MotionDataLoadTask(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale, CallbackBase* callback)
 : AsyncTask(callback),
   mFileUrl(),
   mRawBuffer(nullptr),
   mRawBufferLength(rawBufferLength),
   mScale(scale),
+  mUseRootTranslationOnly(useRootTranslationOnly),
   mAnimationDefinition{},
   mLoadMethod(MotionDataLoadTask::LoadMethod::BVH_BUFFER)
 {
@@ -62,6 +64,7 @@ MotionDataLoadTask::MotionDataLoadTask(const std::string& url, CallbackBase* cal
   mRawBuffer(nullptr),
   mRawBufferLength(0),
   mScale(),
+  mUseRootTranslationOnly(false),
   mAnimationDefinition{},
   mLoadMethod(MotionDataLoadTask::LoadMethod::FACIAL_FILE)
 {
@@ -73,6 +76,7 @@ MotionDataLoadTask::MotionDataLoadTask(const uint8_t* rawBuffer, int rawBufferLe
   mRawBuffer(nullptr),
   mRawBufferLength(rawBufferLength),
   mScale(),
+  mUseRootTranslationOnly(false),
   mAnimationDefinition{},
   mLoadMethod(MotionDataLoadTask::LoadMethod::FACIAL_BUFFER)
 {
@@ -95,12 +99,12 @@ void MotionDataLoadTask::Process()
   {
     case LoadMethod::BVH_FILE:
     {
-      mAnimationDefinition = std::move(Loader::LoadBvh(mFileUrl, "LoadedBvhMotionData", mScale));
+      mAnimationDefinition = std::move(Loader::LoadBvh(mFileUrl, "LoadedBvhMotionData", mUseRootTranslationOnly, mScale));
       break;
     }
     case LoadMethod::BVH_BUFFER:
     {
-      mAnimationDefinition = std::move(Loader::LoadBvhFromBuffer(mRawBuffer, mRawBufferLength, "LoadedBvhMotionData", mScale));
+      mAnimationDefinition = std::move(Loader::LoadBvhFromBuffer(mRawBuffer, mRawBufferLength, "LoadedBvhMotionData", mUseRootTranslationOnly, mScale));
       break;
     }
     case LoadMethod::FACIAL_FILE:
index 89e9f21..2c1d320 100644 (file)
@@ -53,12 +53,12 @@ public:
   /**
    * Constructor for load bvh from file.
    */
-  MotionDataLoadTask(const std::string& path, const Vector3& scale, CallbackBase* callback);
+  MotionDataLoadTask(const std::string& path, bool useRootTranslationOnly, const Vector3& scale, CallbackBase* callback);
 
   /**
    * Constructor for load bvh from buffer.
    */
-  MotionDataLoadTask(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale, CallbackBase* callback);
+  MotionDataLoadTask(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale, CallbackBase* callback);
 
   /**
    * Constructor for load facial from file.
@@ -101,6 +101,7 @@ private:
   uint8_t*    mRawBuffer;
   int         mRawBufferLength;
   Vector3     mScale;
+  bool        mUseRootTranslationOnly;
 
   Scene3D::Loader::AnimationDefinition mAnimationDefinition;
   LoadMethod                           mLoadMethod;
index 0c5922b..b4faf1a 100644 (file)
@@ -359,7 +359,7 @@ bool ParseBvh(std::istream& file, uint32_t& frameCount, float& frameTime, std::s
   return parseHierarchy && parseMotion;
 }
 
-AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale)
+AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, bool useRootTranslationOnly, const Vector3& scale)
 {
   AnimationDefinition animationDefinition;
 
@@ -372,13 +372,19 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
 
   if(!jointList.empty())
   {
-    animationDefinition.ReserveSize(jointList.size() * 2u); // translation and rotation
+    uint32_t animationSize = jointList.size();
+    animationSize = (useRootTranslationOnly) ? (animationSize + 1u) : (animationSize * 2u);
+    animationDefinition.ReserveSize(animationSize);
+    uint32_t animationIndex = 0u;
     for(uint32_t i = 0; i < jointList.size(); ++i)
     {
       AnimatedProperty translationProperty;
-      translationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
-      translationProperty.mNodeName     = jointList[i]->name;
-      translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
+      if(!useRootTranslationOnly || i == 0)
+      {
+        translationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
+        translationProperty.mNodeName     = jointList[i]->name;
+        translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
+      }
 
       AnimatedProperty rotationProperty;
       rotationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
@@ -389,18 +395,24 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
       rotationProperty.mKeyFrames    = Dali::KeyFrames::New();
       for(uint32_t j = 0; j < frameCount; ++j)
       {
-        translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
+        if(!useRootTranslationOnly || i == 0)
+        {
+          translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
+        }
         rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
       }
-      animationDefinition.SetProperty(i * 2u, std::move(translationProperty));
-      animationDefinition.SetProperty(i * 2u + 1, std::move(rotationProperty));
+      if(!useRootTranslationOnly || i == 0)
+      {
+        animationDefinition.SetProperty(animationIndex++, std::move(translationProperty));
+      }
+      animationDefinition.SetProperty(animationIndex++, std::move(rotationProperty));
     }
   }
 
   return animationDefinition;
 }
 
-AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& animationName, const Vector3& scale)
+AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
 {
   uint32_t               frameCount = 0;
   float                  frameTime  = 0.0f;
@@ -412,11 +424,11 @@ AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& ani
     return animationDefinition;
   }
 
-  return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
+  return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, useRootTranslationOnly, scale);
 }
 } // namespace
 
-AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
+AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
 {
   Dali::FileStream fileStream(path);
   std::iostream&   stream = fileStream.GetStream();
@@ -428,10 +440,10 @@ AnimationDefinition LoadBvh(const std::string& path, const std::string& animatio
     return animationDefinition;
   }
 
-  return LoadBvhInternal(stream, animationName, scale);
+  return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
 }
 
-AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, const Vector3& scale)
+AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
 {
   if(rawBuffer == nullptr || rawBufferLength == 0)
   {
@@ -450,6 +462,6 @@ AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLen
     return animationDefinition;
   }
 
-  return LoadBvhInternal(stream, animationName, scale);
+  return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
 }
 } // namespace Dali::Scene3D::Loader
\ No newline at end of file
index d0c5955..58cab5b 100644 (file)
@@ -29,10 +29,11 @@ namespace Dali::Scene3D::Loader
  * @SINCE_2_1.32
  * @param[in] path The file path.
  * @param[in] animationName Name of the motion capture animation
+ * @param[in] useRootTranslationOnly True to use only root translation with rotation animation.
  * @param[in] scale The scale factor to set on the position property manually.
  * @return AnimationDefinition that includes joint animation information.
  */
-DALI_SCENE3D_API AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale = Vector3::ONE);
+DALI_SCENE3D_API AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE);
 
 /**
  * @brief Loads motion capture data from bvh data stream.
@@ -41,10 +42,11 @@ DALI_SCENE3D_API AnimationDefinition LoadBvh(const std::string& path, const std:
  * @param[in] rawBuffer The bvh buffer.
  * @param[in] rawBufferLength The length of buffer.
  * @param[in] animationName Name of the motion capture animation
+ * @param[in] useRootTranslationOnly True to use only root translation with rotation animation.
  * @param[in] scale The scale factor to set on the position property manually.
  * @return AnimationDefinition that includes joint animation information.
  */
-DALI_SCENE3D_API AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, const Vector3& scale = Vector3::ONE);
+DALI_SCENE3D_API AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE);
 
 } // namespace Dali::Scene3D::Loader
 
index f9418b5..ee3c246 100644 (file)
@@ -106,12 +106,22 @@ float MotionData::GetDuration() const
 
 void MotionData::LoadBvh(const std::string& path, const Vector3& scale, bool synchronousLoad)
 {
-  GetImplementation(*this).LoadBvh(path, scale, synchronousLoad);
+  GetImplementation(*this).LoadBvh(path, false, scale, synchronousLoad);
+}
+
+void MotionData::LoadBvh(const std::string& path, bool useRootTranslationOnly, const Vector3& scale, bool synchronousLoad)
+{
+  GetImplementation(*this).LoadBvh(path, useRootTranslationOnly, scale, synchronousLoad);
 }
 
 void MotionData::LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale, bool synchronousLoad)
 {
-  GetImplementation(*this).LoadBvhFromBuffer(rawBuffer, rawBufferLength, scale, synchronousLoad);
+  GetImplementation(*this).LoadBvhFromBuffer(rawBuffer, rawBufferLength, false, scale, synchronousLoad);
+}
+
+void MotionData::LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale, bool synchronousLoad)
+{
+  GetImplementation(*this).LoadBvhFromBuffer(rawBuffer, rawBufferLength, useRootTranslationOnly, scale, synchronousLoad);
 }
 
 void MotionData::LoadFacialAnimation(const std::string& url, bool synchronousLoad)
index 1bdd463..cf4e2e6 100644 (file)
@@ -261,6 +261,19 @@ public: // Public Method
   void LoadBvh(const std::string& path, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
 
   /**
+   * @brief Load MotionData from bvh file.
+   * It will use Dali::Scene3D::Loader::LoadBvh() internally.
+   * LoadCompleteSignal() will be emitted after load completed.
+   *
+   * @SINCE_2_2.34
+   * @param[in] path The file path.
+   * @param[in] scale The scale factor to set on the position property manually.
+   * @param[in] useRootTranslationOnly True to use only root translation with rotation animation.
+   * @param[in] synchronousLoad True if we want to load result synchronously. Default is false.
+   */
+  void LoadBvh(const std::string& path, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
+
+  /**
    * @brief Load MotionData from bvh buffer.
    * It will use Dali::Scene3D::Loader::LoadBvhFromBuffer() internally.
    * LoadCompleteSignal() will be emitted after load completed.
@@ -274,6 +287,20 @@ public: // Public Method
   void LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
 
   /**
+   * @brief Load MotionData from bvh buffer.
+   * It will use Dali::Scene3D::Loader::LoadBvhFromBuffer() internally.
+   * LoadCompleteSignal() will be emitted after load completed.
+   *
+   * @SINCE_2_2.34
+   * @param[in] rawBuffer The bvh buffer containing the facial animation as bvh format string.
+   * @param[in] rawBufferLength The length of buffer.
+   * @param[in] useRootTranslationOnly True to use only root translation with rotation animation.
+   * @param[in] scale The scale factor to set on the position property manually.
+   * @param[in] synchronousLoad True if we want to load result synchronously. Default is false.
+   */
+  void LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, bool useRootTranslationOnly, const Vector3& scale = Vector3::ONE, bool synchronousLoad = false);
+
+  /**
    * @brief Load MotionData from facial defined json file.
    * It will use Dali::Scene3D::Loader::LoadFacialAnimation() internally.
    * LoadCompleteSignal() will be emitted after load completed.