Support RootTranslationOnly option for motion capture data
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / bvh-loader.cpp
index fc6815d..b4faf1a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali-scene3d/public-api/loader/bvh-loader.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/file-stream.h>
+#include <dali/integration-api/debug.h>
+
 #include <fstream>
 #include <iostream>
 #include <memory>
 #include <sstream>
 #include <string_view>
 
-namespace Dali
-{
-namespace Scene3D
-{
-namespace Loader
+namespace Dali::Scene3D::Loader
 {
 namespace
 {
@@ -50,6 +49,7 @@ static constexpr std::string_view TOKEN_ROOT                = "ROOT";
 static constexpr std::string_view TOKEN_MOTION              = "MOTION";
 static constexpr std::string_view PROPERTY_NAME_POSITION    = "position";
 static constexpr std::string_view PROPERTY_NAME_ORIENTATION = "orientation";
+static constexpr std::string_view TOKEN_OPENING_BRACE       = "{";
 static constexpr std::string_view TOKEN_CLOSING_BRACE       = "}";
 
 enum class Channel
@@ -88,9 +88,10 @@ void trim(std::string& s)
           s.end());
 }
 
-void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
+bool ParseHierarchy(std::istream& file, std::shared_ptr<Joint>& joint)
 {
   std::string line;
+  bool        braceExist = false;
   while(std::getline(file, line))
   {
     trim(line);
@@ -143,24 +144,68 @@ void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
       joint->children.push_back(child);
       std::getline(stream, token, ' ');
       child->name = token;
-      ParseHierarchy(file, child);
+
+      if(DALI_UNLIKELY(!ParseHierarchy(file, child)))
+      {
+        return false;
+      }
     }
     else if(line == TOKEN_END_SITE.data())
     {
+      bool braceExistEndSite = false;
       while(std::getline(file, line))
       {
         trim(line);
-        if(line == TOKEN_CLOSING_BRACE.data())
+        if(line == TOKEN_OPENING_BRACE.data())
+        {
+          if(DALI_UNLIKELY(braceExistEndSite))
+          {
+            DALI_LOG_ERROR("Parsing error : Joint[%s] End Site opening brace not matched\n", joint->name.c_str());
+            return false;
+          }
+          braceExistEndSite = true;
+        }
+        else if(line == TOKEN_CLOSING_BRACE.data())
         {
+          if(DALI_UNLIKELY(!braceExistEndSite))
+          {
+            DALI_LOG_ERROR("Parsing error : Joint[%s] End Site closing brace not matched\n", joint->name.c_str());
+            return false;
+          }
           break;
         }
       }
+      if(DALI_UNLIKELY(!braceExistEndSite))
+      {
+        DALI_LOG_ERROR("Parsing error : Joint[%s] End Site opening brace not exist\n", joint->name.c_str());
+        return false;
+      }
+    }
+    else if(token == TOKEN_OPENING_BRACE.data())
+    {
+      if(DALI_UNLIKELY(braceExist))
+      {
+        DALI_LOG_ERROR("Parsing error : Joint[%s] opening brace not matched\n", joint->name.c_str());
+        return false;
+      }
+      braceExist = true;
     }
     else if(token == TOKEN_CLOSING_BRACE.data())
     {
+      if(DALI_UNLIKELY(!braceExist))
+      {
+        DALI_LOG_ERROR("Parsing error : Joint[%s] closing brace not matched\n", joint->name.c_str());
+        return false;
+      }
       break;
     }
   }
+  if(DALI_UNLIKELY(!braceExist))
+  {
+    DALI_LOG_ERROR("Parsing error : Joint[%s] opening brace not exist\n", joint->name.c_str());
+    return false;
+  }
+  return true;
 }
 
 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
@@ -172,7 +217,7 @@ void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>
   }
 }
 
-void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
+bool ParseMotion(std::istream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
 {
   std::vector<std::shared_ptr<Joint>> jointList;
   MakeList(hierarchy, jointList);
@@ -199,10 +244,33 @@ void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_
     }
   }
 
-  while(getline(file, line))
+  if(DALI_UNLIKELY(!frameCountLoaded))
+  {
+    DALI_LOG_ERROR("Parsing error : Frames not exist!\n");
+    return false;
+  }
+  if(DALI_UNLIKELY(!frameTimeLoaded))
+  {
+    DALI_LOG_ERROR("Parsing error : Frame Time not exist!\n");
+    return false;
+  }
+
+  uint32_t loadedFrameCount = 0u;
+
+  while(std::getline(file, line))
   {
     trim(line);
+    if(DALI_UNLIKELY(line.empty()))
+    {
+      continue;
+    }
     std::istringstream stream(line);
+    if(DALI_UNLIKELY(++loadedFrameCount > frameCount))
+    {
+      // Parse failed. Just skip decoding, and get the number of line for debug.
+      continue;
+    }
+
     for(auto&& joint : jointList)
     {
       Vector3    translation;
@@ -244,18 +312,22 @@ void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_
       joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
     }
   }
-}
 
-bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
-{
-  std::ifstream file(path.data());
-  if(!file.is_open())
+  if(DALI_UNLIKELY(loadedFrameCount != frameCount))
   {
+    DALI_LOG_ERROR("Parsing error : Motion frame count not matched! expect : %u, loaded : %u\n", frameCount, loadedFrameCount);
     return false;
   }
 
+  return true;
+}
+
+bool ParseBvh(std::istream& file, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
+{
   std::string line;
-  while(getline(file, line))
+  bool        parseHierarchy = false;
+  bool        parseMotion    = false;
+  while(std::getline(file, line))
   {
     trim(line);
     std::istringstream stream(line);
@@ -264,7 +336,7 @@ bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, s
     if(token == TOKEN_HIERARCHY.data())
     {
       std::string line;
-      while(getline(file, line))
+      while(std::getline(file, line))
       {
         trim(line);
         std::istringstream stream(line);
@@ -274,72 +346,122 @@ bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, s
         {
           std::getline(stream, token, ' ');
           rootJoint->name = token;
-          ParseHierarchy(file, rootJoint);
+          parseHierarchy  = ParseHierarchy(file, rootJoint);
           break;
         }
       }
     }
     if(token == TOKEN_MOTION.data())
     {
-      ParseMotion(file, rootJoint, frameCount, frameTime);
+      parseMotion = ParseMotion(file, rootJoint, frameCount, frameTime);
     }
   }
-  return true;
+  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;
 
-  animationDefinition.mName     = animationName;
-  animationDefinition.mDuration = frameTime * (frameCount - 1);
-  float keyFrameInterval        = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
+  animationDefinition.SetName(animationName);
+  animationDefinition.SetDuration(frameTime * (frameCount - 1));
+  float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
 
   std::vector<std::shared_ptr<Joint>> jointList;
   MakeList(hierarchy, jointList);
 
   if(!jointList.empty())
   {
-    animationDefinition.mProperties.resize(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 = animationDefinition.mProperties[i * 2u];
-      translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
-      translationProperty.mNodeName         = jointList[i]->name;
-      translationProperty.mPropertyName     = PROPERTY_NAME_POSITION.data();
+      AnimatedProperty translationProperty;
+      if(!useRootTranslationOnly || i == 0)
+      {
+        translationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
+        translationProperty.mNodeName     = jointList[i]->name;
+        translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
+      }
 
-      AnimatedProperty& rotationProperty = animationDefinition.mProperties[i * 2u + 1];
-      rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
-      rotationProperty.mNodeName         = jointList[i]->name;
-      rotationProperty.mPropertyName     = PROPERTY_NAME_ORIENTATION.data();
+      AnimatedProperty rotationProperty;
+      rotationProperty.mTimePeriod   = Dali::TimePeriod(animationDefinition.GetDuration());
+      rotationProperty.mNodeName     = jointList[i]->name;
+      rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
 
       translationProperty.mKeyFrames = Dali::KeyFrames::New();
       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]);
       }
+      if(!useRootTranslationOnly || i == 0)
+      {
+        animationDefinition.SetProperty(animationIndex++, std::move(translationProperty));
+      }
+      animationDefinition.SetProperty(animationIndex++, std::move(rotationProperty));
     }
   }
 
   return animationDefinition;
 }
-} // namespace
 
-AnimationDefinition LoadBvh(const std::string& path, 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;
   std::shared_ptr<Joint> rootJoint(new Joint);
-  if(!ParseBvh(path, frameCount, frameTime, rootJoint))
+
+  if(!ParseBvh(stream, frameCount, frameTime, rootJoint))
+  {
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, useRootTranslationOnly, scale);
+}
+} // namespace
+
+AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
+{
+  Dali::FileStream fileStream(path);
+  std::iostream&   stream = fileStream.GetStream();
+
+  if(stream.fail())
+  {
+    DALI_LOG_ERROR("Fail to load bvh file : %s\n", path.c_str());
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
+}
+
+AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
+{
+  if(rawBuffer == nullptr || rawBufferLength == 0)
+  {
+    DALI_LOG_ERROR("Fail to load bvh buffer : buffer is empty!\n");
+    AnimationDefinition animationDefinition;
+    return animationDefinition;
+  }
+
+  Dali::FileStream fileStream(const_cast<uint8_t*>(rawBuffer), static_cast<size_t>(static_cast<uint32_t>(rawBufferLength)));
+  std::iostream&   stream = fileStream.GetStream();
+
+  if(stream.fail())
   {
+    DALI_LOG_ERROR("Fail to load bvh buffer : buffer length : %d\n", rawBufferLength);
     AnimationDefinition animationDefinition;
     return animationDefinition;
   }
 
-  return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
+  return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
 }
-} // namespace Loader
-} // namespace Scene3D
-} // namespace Dali
\ No newline at end of file
+} // namespace Dali::Scene3D::Loader
\ No newline at end of file