dfd9cc2633c331efd24293088b4d87c734b3f07c
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / facial-animation-loader.cpp
1 /*\r
2  * Copyright (c) 2022 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 <sstream>\r
23 \r
24 // INTERNAL INCLUDES\r
25 #include <dali-scene3d/internal/loader/json-reader.h>\r
26 #include <dali-scene3d/public-api/loader/blend-shape-details.h>\r
27 #include <dali-scene3d/public-api/loader/utils.h>\r
28 \r
29 namespace js = json;\r
30 \r
31 namespace Dali\r
32 {\r
33 namespace\r
34 {\r
35 const float MILLISECONDS_TO_SECONDS = 0.001f;\r
36 \r
37 struct BlendShape\r
38 {\r
39   std::vector<std::vector<float>> mKeys;\r
40   std::string_view                mNodeName;\r
41   uint32_t                        mNumberOfMorphTarget;\r
42   std::string_view                mVersion;\r
43   std::string_view                mFullName;\r
44   std::vector<std::string_view>   mMorphNames;\r
45 };\r
46 \r
47 struct FacialAnimation\r
48 {\r
49   std::string_view        mName;\r
50   std::vector<BlendShape> mBlendShapes;\r
51   std::string_view        mVersion;\r
52   uint32_t                mNumberOfShapes;\r
53   std::vector<uint32_t>   mTime;\r
54   uint32_t                mNumberOfFrames;\r
55 };\r
56 \r
57 std::vector<std::vector<float>> ReadBlendShapeKeys(const json_value_s& j)\r
58 {\r
59   auto&                           jo = js::Cast<json_array_s>(j);\r
60   std::vector<std::vector<float>> result;\r
61 \r
62   result.reserve(jo.length);\r
63 \r
64   auto i = jo.start;\r
65   while(i)\r
66   {\r
67     result.push_back(std::move(js::Read::Array<float, js::Read::Number>(*i->value)));\r
68     i = i->next;\r
69   }\r
70 \r
71   return result;\r
72 }\r
73 \r
74 const js::Reader<BlendShape>& GetBlendShapeReader()\r
75 {\r
76   static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
77                                                       .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
78                                                       .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
79                                                       .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
80                                                       .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
81                                                       .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
82                                                       .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
83   return BLEND_SHAPE_READER;\r
84 }\r
85 \r
86 const js::Reader<FacialAnimation>& GetFacialAnimationReader()\r
87 {\r
88   static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
89                                                            .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
90                                                            .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
91                                                            .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
92                                                            .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
93                                                            .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
94                                                            .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
95   return FACIAL_ANIMATION_READER;\r
96 }\r
97 \r
98 } // unnamed namespace\r
99 \r
100 namespace Scene3D\r
101 {\r
102 namespace Loader\r
103 {\r
104 AnimationDefinition LoadFacialAnimation(const std::string& url)\r
105 {\r
106   bool failed = false;\r
107   auto js     = LoadTextFile(url.c_str(), &failed);\r
108   if(failed)\r
109   {\r
110     ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
111   }\r
112 \r
113   json::unique_ptr root(json_parse(js.c_str(), js.size()));\r
114   if(!root)\r
115   {\r
116     ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse " << url << ".";\r
117   }\r
118 \r
119   static bool setObjectReaders = true;\r
120   if(setObjectReaders)\r
121   {\r
122     // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
123     js::SetObjectReader(GetBlendShapeReader());\r
124     setObjectReaders = false;\r
125   }\r
126 \r
127   auto& rootObj = js::Cast<json_object_s>(*root);\r
128 \r
129   FacialAnimation facialAnimation;\r
130   GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
131 \r
132   AnimationDefinition animationDefinition;\r
133   animationDefinition.mName     = std::string(facialAnimation.mName.data());\r
134   animationDefinition.mDuration = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]);\r
135 \r
136   // Calculate the number of animated properties.\r
137   uint32_t numberOfAnimatedProperties = 0u;\r
138   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
139   {\r
140     numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
141   }\r
142   animationDefinition.mProperties.resize(numberOfAnimatedProperties);\r
143 \r
144   // Create the key frame instances.\r
145   for(auto& animatedProperty : animationDefinition.mProperties)\r
146   {\r
147     animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
148   }\r
149 \r
150   // Set the property names\r
151   uint32_t targets = 0u;\r
152   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
153   {\r
154     for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
155     {\r
156       AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
157       animatedProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);\r
158 \r
159       animatedProperty.mNodeName = blendShape.mNodeName;\r
160 \r
161       std::stringstream weightPropertyStream;\r
162       weightPropertyStream << BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
163       animatedProperty.mPropertyName = weightPropertyStream.str();\r
164     }\r
165     targets += blendShape.mNumberOfMorphTarget;\r
166   }\r
167 \r
168   targets = 0u;\r
169   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
170   {\r
171     for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
172     {\r
173       const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.mDuration;\r
174 \r
175       for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
176       {\r
177         AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
178 \r
179         animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
180       }\r
181     }\r
182     targets += blendShape.mNumberOfMorphTarget;\r
183   }\r
184 \r
185   return animationDefinition;\r
186 }\r
187 \r
188 } // namespace Loader\r
189 } // namespace Scene3D\r
190 } // namespace Dali\r