X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-scene3d%2Fpublic-api%2Floader%2Fbvh-loader.cpp;h=d9bc6f2d8df0bd16b1635ccb8c5885e8b1b10888;hb=HEAD;hp=93a826dae50c0b060f123775c7f5c29e84859f19;hpb=dca138975014fcdee6e45f541551d99aa406cbf0;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-scene3d/public-api/loader/bvh-loader.cpp b/dali-scene3d/public-api/loader/bvh-loader.cpp index 93a826d..d9bc6f2 100644 --- a/dali-scene3d/public-api/loader/bvh-loader.cpp +++ b/dali-scene3d/public-api/loader/bvh-loader.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -19,6 +19,10 @@ #include // EXTERNAL INCLUDES +#include +#include +#include + #include #include #include @@ -46,6 +50,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 @@ -84,9 +89,10 @@ void trim(std::string& s) s.end()); } -void ParseHierarchy(std::ifstream& file, std::shared_ptr& joint) +bool ParseHierarchy(std::istream& file, std::shared_ptr& joint) { std::string line; + bool braceExist = false; while(std::getline(file, line)) { trim(line); @@ -139,24 +145,68 @@ void ParseHierarchy(std::ifstream& file, std::shared_ptr& 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, std::vector>& jointList) @@ -168,7 +218,7 @@ void MakeList(std::shared_ptr& joint, std::vector> } } -void ParseMotion(std::ifstream& file, std::shared_ptr& hierarchy, uint32_t& frameCount, float& frameTime) +bool ParseMotion(std::istream& file, std::shared_ptr& hierarchy, uint32_t& frameCount, float& frameTime) { std::vector> jointList; MakeList(hierarchy, jointList); @@ -195,10 +245,33 @@ void ParseMotion(std::ifstream& file, std::shared_ptr& 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; @@ -240,18 +313,22 @@ void ParseMotion(std::ifstream& file, std::shared_ptr& 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& 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& 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); @@ -260,7 +337,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); @@ -270,70 +347,126 @@ 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& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale) +AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr& 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(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10; + animationDefinition.SetName(animationName); + animationDefinition.SetDuration(frameTime * (frameCount - 1)); + float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10; std::vector> 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(j) * keyFrameInterval, (jointList[i]->translations[j] * scale)); + if(!useRootTranslationOnly || i == 0) + { + translationProperty.mKeyFrames.Add(static_cast(j) * keyFrameInterval, (jointList[i]->translations[j] * scale)); + } rotationProperty.mKeyFrames.Add(static_cast(j) * keyFrameInterval, jointList[i]->rotations[j]); } + 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 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(rawBuffer), static_cast(static_cast(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