[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / facial-animation-loader.cpp
1 /*\r
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  * http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  *\r
16  */\r
17 \r
18 // FILE HEADER\r
19 #include <dali-scene3d/public-api/loader/facial-animation-loader.h>\r
20 \r
21 // EXTERNAL INCLUDES\r
22 #include <dali/devel-api/animation/key-frames-devel.h>\r
23 #include <sstream>\r
24 \r
25 // INTERNAL INCLUDES\r
26 #include <dali-scene3d/internal/loader/json-reader.h>\r
27 #include <dali-scene3d/public-api/loader/blend-shape-details.h>\r
28 #include <dali-scene3d/public-api/loader/utils.h>\r
29 \r
30 namespace js = json;\r
31 \r
32 namespace Dali\r
33 {\r
34 namespace\r
35 {\r
36 const float MILLISECONDS_TO_SECONDS = 0.001f;\r
37 \r
38 struct BlendShape\r
39 {\r
40   std::vector<std::vector<float>> mKeys;\r
41   std::string_view                mNodeName;\r
42   uint32_t                        mNumberOfMorphTarget;\r
43   std::string_view                mVersion;\r
44   std::string_view                mFullName;\r
45   std::vector<std::string_view>   mMorphNames;\r
46 };\r
47 \r
48 struct FacialAnimation\r
49 {\r
50   std::string_view        mName;\r
51   std::vector<BlendShape> mBlendShapes;\r
52   std::string_view        mVersion;\r
53   uint32_t                mNumberOfShapes;\r
54   std::vector<uint32_t>   mTime;\r
55   uint32_t                mNumberOfFrames;\r
56 };\r
57 \r
58 std::vector<std::vector<float>> ReadBlendShapeKeys(const json_value_s& j)\r
59 {\r
60   auto&                           jo = js::Cast<json_array_s>(j);\r
61   std::vector<std::vector<float>> result;\r
62 \r
63   result.reserve(jo.length);\r
64 \r
65   auto i = jo.start;\r
66   while(i)\r
67   {\r
68     result.push_back(std::move(js::Read::Array<float, js::Read::Number>(*i->value)));\r
69     i = i->next;\r
70   }\r
71 \r
72   return result;\r
73 }\r
74 \r
75 const js::Reader<BlendShape>& GetBlendShapeReader()\r
76 {\r
77   static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
78                                                      .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
79                                                      .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
80                                                      .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
81                                                      .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
82                                                      .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
83                                                      .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
84   return BLEND_SHAPE_READER;\r
85 }\r
86 \r
87 const js::Reader<FacialAnimation>& GetFacialAnimationReader()\r
88 {\r
89   static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
90                                                           .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
91                                                           .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
92                                                           .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
93                                                           .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
94                                                           .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
95                                                           .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
96   return FACIAL_ANIMATION_READER;\r
97 }\r
98 \r
99 Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::unique_ptr& root)\r
100 {\r
101   static bool setObjectReaders = true;\r
102   if(setObjectReaders)\r
103   {\r
104     // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
105     js::SetObjectReader(GetBlendShapeReader());\r
106     setObjectReaders = false;\r
107   }\r
108 \r
109   auto& rootObj = js::Cast<json_object_s>(*root);\r
110 \r
111   FacialAnimation facialAnimation;\r
112   GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
113 \r
114   Dali::Scene3D::Loader::AnimationDefinition animationDefinition;\r
115   animationDefinition.SetName(facialAnimation.mName.data());\r
116   animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
117 \r
118   // Calculate the number of animated properties.\r
119   uint32_t numberOfAnimatedProperties = 0u;\r
120   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
121   {\r
122     numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
123   }\r
124   animationDefinition.ReserveSize(numberOfAnimatedProperties);\r
125 \r
126   uint32_t targets = 0u;\r
127   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
128   {\r
129     for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
130     {\r
131       Dali::Scene3D::Loader::AnimatedProperty animatedProperty;\r
132       animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());\r
133       animatedProperty.mNodeName   = blendShape.mNodeName;\r
134       std::stringstream weightPropertyStream;\r
135       weightPropertyStream << Dali::Scene3D::Loader::BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
136       animatedProperty.mPropertyName = weightPropertyStream.str();\r
137 \r
138       animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
139       for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
140       {\r
141         const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
142         animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
143       }\r
144       // Optimize keyframes, for heuristic!\r
145       DevelKeyFrames::OptimizeKeyFramesLinear(animatedProperty.mKeyFrames);\r
146       animationDefinition.SetProperty(targets + morphTargetIndex, std::move(animatedProperty));\r
147     }\r
148 \r
149     targets += blendShape.mNumberOfMorphTarget;\r
150   }\r
151 \r
152   return animationDefinition;\r
153 }\r
154 } // unnamed namespace\r
155 \r
156 namespace Scene3D\r
157 {\r
158 namespace Loader\r
159 {\r
160 AnimationDefinition LoadFacialAnimation(const std::string& url)\r
161 {\r
162   bool failed   = false;\r
163   auto jsonData = LoadTextFile(url.c_str(), &failed);\r
164   if(failed)\r
165   {\r
166     ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
167   }\r
168 \r
169   json::unique_ptr root(json_parse(jsonData.c_str(), jsonData.size()));\r
170   if(!root)\r
171   {\r
172     ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json " << url << ".";\r
173   }\r
174 \r
175   return LoadFacialAnimationInternal(root);\r
176 }\r
177 \r
178 AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength)\r
179 {\r
180   json::unique_ptr root(json_parse(rawBuffer, static_cast<size_t>(static_cast<uint32_t>(rawBufferLength))));\r
181   if(!root)\r
182   {\r
183     ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json from buffer.";\r
184   }\r
185 \r
186   return LoadFacialAnimationInternal(root);\r
187 }\r
188 } // namespace Loader\r
189 } // namespace Scene3D\r
190 } // namespace Dali\r