[Tizen] Load bvh and facial animations from buffer
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / facial-animation-loader.cpp
1 /*\r
2  * Copyright (c) 2023 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 Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::unique_ptr& root)\r
99 {\r
100   static bool setObjectReaders = true;\r
101   if(setObjectReaders)\r
102   {\r
103     // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
104     js::SetObjectReader(GetBlendShapeReader());\r
105     setObjectReaders = false;\r
106   }\r
107 \r
108   auto& rootObj = js::Cast<json_object_s>(*root);\r
109 \r
110   FacialAnimation facialAnimation;\r
111   GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
112 \r
113   Dali::Scene3D::Loader::AnimationDefinition animationDefinition;\r
114   animationDefinition.SetName(facialAnimation.mName.data());\r
115   animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
116 \r
117   // Calculate the number of animated properties.\r
118   uint32_t numberOfAnimatedProperties = 0u;\r
119   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
120   {\r
121     numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
122   }\r
123   animationDefinition.ReserveSize(numberOfAnimatedProperties);\r
124 \r
125   uint32_t targets = 0u;\r
126   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
127   {\r
128     for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
129     {\r
130       Dali::Scene3D::Loader::AnimatedProperty animatedProperty;\r
131       animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());\r
132       animatedProperty.mNodeName   = blendShape.mNodeName;\r
133       std::stringstream weightPropertyStream;\r
134       weightPropertyStream << Dali::Scene3D::Loader::BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
135       animatedProperty.mPropertyName = weightPropertyStream.str();\r
136 \r
137       animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
138       for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
139       {\r
140         const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
141         animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
142       }\r
143       animationDefinition.SetProperty(targets + morphTargetIndex, std::move(animatedProperty));\r
144     }\r
145 \r
146     targets += blendShape.mNumberOfMorphTarget;\r
147   }\r
148 \r
149   return animationDefinition;\r
150 }\r
151 } // unnamed namespace\r
152 \r
153 namespace Scene3D\r
154 {\r
155 namespace Loader\r
156 {\r
157 AnimationDefinition LoadFacialAnimation(const std::string& url)\r
158 {\r
159   bool failed   = false;\r
160   auto jsonData = LoadTextFile(url.c_str(), &failed);\r
161   if(failed)\r
162   {\r
163     ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";\r
164   }\r
165 \r
166   json::unique_ptr root(json_parse(jsonData.c_str(), jsonData.size()));\r
167   if(!root)\r
168   {\r
169     ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json " << url << ".";\r
170   }\r
171 \r
172   return LoadFacialAnimationInternal(root);\r
173 }\r
174 \r
175 AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength)\r
176 {\r
177   json::unique_ptr root(json_parse(rawBuffer, static_cast<size_t>(static_cast<uint32_t>(rawBufferLength))));\r
178   if(!root)\r
179   {\r
180     ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json from buffer.";\r
181   }\r
182 \r
183   return LoadFacialAnimationInternal(root);\r
184 }\r
185 } // namespace Loader\r
186 } // namespace Scene3D\r
187 } // namespace Dali\r