/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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/devel-api/animation/key-frames-devel.h>
+#include <dali/integration-api/debug.h>
+
#include <fstream>
#include <iostream>
#include <memory>
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
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);
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)
}
}
-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);
}
}
- 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;
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);
if(token == TOKEN_HIERARCHY.data())
{
std::string line;
- while(getline(file, line))
+ while(std::getline(file, line))
{
trim(line);
std::istringstream stream(line);
{
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.SetName(animationName);
animationDefinition.SetDuration(frameTime * (frameCount - 1));
- float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
+ 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.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());
- rotationProperty.mNodeName = jointList[i]->name;
- rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
+ 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]);
}
- animationDefinition.SetProperty(i * 2u, std::move(translationProperty));
- animationDefinition.SetProperty(i * 2u + 1, std::move(rotationProperty));
+ if(!useRootTranslationOnly || i == 0)
+ {
+ // Optimize keyframes, for heuristic!
+ DevelKeyFrames::OptimizeKeyFramesLinear(translationProperty.mKeyFrames);
+ animationDefinition.SetProperty(animationIndex++, std::move(translationProperty));
+ }
+ // Optimize keyframes, for heuristic!
+ DevelKeyFrames::OptimizeKeyFramesLinear(rotationProperty.mKeyFrames);
+ 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 Dali::Scene3D::Loader
\ No newline at end of file