/*\r
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
#include <dali-scene3d/public-api/loader/facial-animation-loader.h>\r
\r
// EXTERNAL INCLUDES\r
+#include <dali/devel-api/animation/key-frames-devel.h>\r
#include <sstream>\r
\r
// INTERNAL INCLUDES\r
return result;\r
}\r
\r
-const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
- .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
- .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
- .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
- .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
- .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
- .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
-\r
-const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
- .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
- .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
- .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
- .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
- .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
- .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
-\r
-} // unnamed namespace\r
-\r
-namespace Scene3D\r
-{\r
-namespace Loader\r
-{\r
-AnimationDefinition LoadFacialAnimation(const std::string& url)\r
+const js::Reader<BlendShape>& GetBlendShapeReader()\r
{\r
- bool failed = false;\r
- auto js = LoadTextFile(url.c_str(), &failed);\r
- if(failed)\r
- {\r
- ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
- }\r
+ static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
+ .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
+ .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
+ .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
+ return BLEND_SHAPE_READER;\r
+}\r
\r
- json::unique_ptr root(json_parse(js.c_str(), js.size()));\r
- if(!root)\r
- {\r
- ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse " << url << ".";\r
- }\r
+const js::Reader<FacialAnimation>& GetFacialAnimationReader()\r
+{\r
+ static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
+ .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
+ .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
+ .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
+ .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
+ .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
+ .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
+ return FACIAL_ANIMATION_READER;\r
+}\r
\r
+Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::unique_ptr& root)\r
+{\r
static bool setObjectReaders = true;\r
if(setObjectReaders)\r
{\r
// NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
- js::SetObjectReader(BLEND_SHAPE_READER);\r
+ js::SetObjectReader(GetBlendShapeReader());\r
setObjectReaders = false;\r
}\r
\r
auto& rootObj = js::Cast<json_object_s>(*root);\r
\r
FacialAnimation facialAnimation;\r
- FACIAL_ANIMATION_READER.Read(rootObj, facialAnimation);\r
+ GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
\r
- AnimationDefinition animationDefinition;\r
- animationDefinition.mName = std::string(facialAnimation.mName.data());\r
- animationDefinition.mDuration = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]);\r
+ Dali::Scene3D::Loader::AnimationDefinition animationDefinition;\r
+ animationDefinition.SetName(facialAnimation.mName.data());\r
+ animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
\r
// Calculate the number of animated properties.\r
uint32_t numberOfAnimatedProperties = 0u;\r
{\r
numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
}\r
- animationDefinition.mProperties.resize(numberOfAnimatedProperties);\r
-\r
- // Create the key frame instances.\r
- for(auto& animatedProperty : animationDefinition.mProperties)\r
- {\r
- animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
- }\r
+ animationDefinition.ReserveSize(numberOfAnimatedProperties);\r
\r
- // Set the property names\r
uint32_t targets = 0u;\r
for(const auto& blendShape : facialAnimation.mBlendShapes)\r
{\r
for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
{\r
- AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
- animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.mDuration);\r
-\r
- animatedProperty.mNodeName = blendShape.mNodeName;\r
-\r
+ Dali::Scene3D::Loader::AnimatedProperty animatedProperty;\r
+ animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());\r
+ animatedProperty.mNodeName = blendShape.mNodeName;\r
std::stringstream weightPropertyStream;\r
- weightPropertyStream << BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
+ weightPropertyStream << Dali::Scene3D::Loader::BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
animatedProperty.mPropertyName = weightPropertyStream.str();\r
- }\r
- targets += blendShape.mNumberOfMorphTarget;\r
- }\r
\r
- targets = 0u;\r
- for(const auto& blendShape : facialAnimation.mBlendShapes)\r
- {\r
- for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
- {\r
- const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.mDuration;\r
-\r
- for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
+ animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
+ for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
{\r
- AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
-\r
+ const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
}\r
+ // Optimize keyframes, for heuristic!\r
+ DevelKeyFrames::OptimizeKeyFramesLinear(animatedProperty.mKeyFrames);\r
+ animationDefinition.SetProperty(targets + morphTargetIndex, std::move(animatedProperty));\r
}\r
+\r
targets += blendShape.mNumberOfMorphTarget;\r
}\r
\r
return animationDefinition;\r
}\r
+} // unnamed namespace\r
+\r
+namespace Scene3D\r
+{\r
+namespace Loader\r
+{\r
+AnimationDefinition LoadFacialAnimation(const std::string& url)\r
+{\r
+ bool failed = false;\r
+ auto jsonData = LoadTextFile(url.c_str(), &failed);\r
+ if(failed)\r
+ {\r
+ ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
+ }\r
+\r
+ json::unique_ptr root(json_parse(jsonData.c_str(), jsonData.size()));\r
+ if(!root)\r
+ {\r
+ ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json " << url << ".";\r
+ }\r
+\r
+ return LoadFacialAnimationInternal(root);\r
+}\r
\r
+AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength)\r
+{\r
+ json::unique_ptr root(json_parse(rawBuffer, static_cast<size_t>(static_cast<uint32_t>(rawBufferLength))));\r
+ if(!root)\r
+ {\r
+ ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json from buffer.";\r
+ }\r
+\r
+ return LoadFacialAnimationInternal(root);\r
+}\r
} // namespace Loader\r
} // namespace Scene3D\r
} // namespace Dali\r