Merge "Fix: VS15 hides non-Emoji Negative Squared Latin Capital Letter" into devel...
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / bvh-loader.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // FILE HEADER
19 #include <dali-scene3d/public-api/loader/bvh-loader.h>
20
21 // EXTERNAL INCLUDES
22 #include <fstream>
23 #include <iostream>
24 #include <memory>
25 #include <sstream>
26 #include <string_view>
27
28 namespace Dali
29 {
30 namespace Scene3D
31 {
32 namespace Loader
33 {
34 namespace
35 {
36 static constexpr std::string_view TOKEN_OFFSET              = "OFFSET";
37 static constexpr std::string_view TOKEN_CHANNELS            = "CHANNELS";
38 static constexpr std::string_view TOKEN_XPOSITION           = "Xposition";
39 static constexpr std::string_view TOKEN_YPOSITION           = "Yposition";
40 static constexpr std::string_view TOKEN_ZPOSITION           = "Zposition";
41 static constexpr std::string_view TOKEN_XROTATION           = "Xrotation";
42 static constexpr std::string_view TOKEN_YROTATION           = "Yrotation";
43 static constexpr std::string_view TOKEN_ZROTATION           = "Zrotation";
44 static constexpr std::string_view TOKEN_JOINT               = "JOINT";
45 static constexpr std::string_view TOKEN_END_SITE            = "End Site";
46 static constexpr std::string_view TOKEN_FRAMES              = "Frames";
47 static constexpr std::string_view TOKEN_FRAME_TIME          = "Frame Time";
48 static constexpr std::string_view TOKEN_HIERARCHY           = "HIERARCHY";
49 static constexpr std::string_view TOKEN_ROOT                = "ROOT";
50 static constexpr std::string_view TOKEN_MOTION              = "MOTION";
51 static constexpr std::string_view PROPERTY_NAME_POSITION    = "position";
52 static constexpr std::string_view PROPERTY_NAME_ORIENTATION = "orientation";
53 static constexpr std::string_view TOKEN_CLOSING_BRACE       = "}";
54
55 enum class Channel
56 {
57   XPOSITION = 0,
58   YPOSITION,
59   ZPOSITION,
60   XROTATION,
61   YROTATION,
62   ZROTATION
63 };
64
65 struct Frame
66 {
67   std::vector<float> values;
68 };
69
70 struct Joint
71 {
72   std::string                         name;
73   Vector3                             offset;
74   std::vector<Vector3>                translations;
75   std::vector<Quaternion>             rotations;
76   std::vector<Channel>                channels;
77   std::vector<std::shared_ptr<Joint>> children;
78 };
79
80 void trim(std::string& s)
81 {
82   s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
83             return !std::isspace(ch);
84           }));
85   s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
86             return !std::isspace(ch);
87           }).base(),
88           s.end());
89 }
90
91 void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
92 {
93   std::string line;
94   while(std::getline(file, line))
95   {
96     trim(line);
97     std::istringstream stream(line);
98     std::string        token;
99     std::getline(stream, token, ' ');
100
101     if(token == TOKEN_OFFSET.data())
102     {
103       stream >> joint->offset.x >> joint->offset.y >> joint->offset.z;
104     }
105     else if(token == TOKEN_CHANNELS.data())
106     {
107       uint32_t channelCount = 0;
108       std::getline(stream, token, ' ');
109       channelCount = static_cast<uint32_t>(std::atoi(token.c_str()));
110       for(uint32_t i = 0; i < channelCount; ++i)
111       {
112         std::getline(stream, token, ' ');
113         trim(token);
114         if(token == TOKEN_XPOSITION.data())
115         {
116           joint->channels.push_back(Channel::XPOSITION);
117         }
118         else if(token == TOKEN_YPOSITION.data())
119         {
120           joint->channels.push_back(Channel::YPOSITION);
121         }
122         else if(token == TOKEN_ZPOSITION.data())
123         {
124           joint->channels.push_back(Channel::ZPOSITION);
125         }
126         else if(token == TOKEN_XROTATION.data())
127         {
128           joint->channels.push_back(Channel::XROTATION);
129         }
130         else if(token == TOKEN_YROTATION.data())
131         {
132           joint->channels.push_back(Channel::YROTATION);
133         }
134         else if(token == TOKEN_ZROTATION.data())
135         {
136           joint->channels.push_back(Channel::ZROTATION);
137         }
138       }
139     }
140     else if(token == TOKEN_JOINT.data())
141     {
142       std::shared_ptr<Joint> child(new Joint);
143       joint->children.push_back(child);
144       std::getline(stream, token, ' ');
145       child->name = token;
146       ParseHierarchy(file, child);
147     }
148     else if(line == TOKEN_END_SITE.data())
149     {
150       while(std::getline(file, line))
151       {
152         trim(line);
153         if(line == TOKEN_CLOSING_BRACE.data())
154         {
155           break;
156         }
157       }
158     }
159     else if(token == TOKEN_CLOSING_BRACE.data())
160     {
161       break;
162     }
163   }
164 }
165
166 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
167 {
168   jointList.push_back(joint);
169   for(uint32_t i = 0; i < joint->children.size(); ++i)
170   {
171     MakeList(joint->children[i], jointList);
172   }
173 }
174
175 void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
176 {
177   std::vector<std::shared_ptr<Joint>> jointList;
178   MakeList(hierarchy, jointList);
179
180   bool        frameCountLoaded = false;
181   bool        frameTimeLoaded  = false;
182   std::string line;
183   while((!frameCountLoaded || !frameTimeLoaded) && std::getline(file, line))
184   {
185     trim(line);
186     std::istringstream stream(line);
187     std::string        token;
188     std::getline(stream, token, ':');
189     trim(token);
190     if(token == TOKEN_FRAMES.data())
191     {
192       stream >> frameCount;
193       frameCountLoaded = true;
194     }
195     else if(token == TOKEN_FRAME_TIME.data())
196     {
197       stream >> frameTime;
198       frameTimeLoaded = true;
199     }
200   }
201
202   while(getline(file, line))
203   {
204     trim(line);
205     std::istringstream stream(line);
206     for(auto&& joint : jointList)
207     {
208       Vector3    translation;
209       Quaternion rotation[3];
210       for(uint32_t i = 0; i < joint->channels.size(); ++i)
211       {
212         if(joint->channels[i] == Channel::XPOSITION)
213         {
214           stream >> translation.x;
215         }
216         else if(joint->channels[i] == Channel::YPOSITION)
217         {
218           stream >> translation.y;
219         }
220         else if(joint->channels[i] == Channel::ZPOSITION)
221         {
222           stream >> translation.z;
223         }
224         else if(joint->channels[i] == Channel::XROTATION)
225         {
226           float radian;
227           stream >> radian;
228           rotation[0] = Quaternion(Radian(Degree(radian)), Vector3::XAXIS);
229         }
230         else if(joint->channels[i] == Channel::YROTATION)
231         {
232           float radian;
233           stream >> radian;
234           rotation[1] = Quaternion(Radian(Degree(radian)), Vector3::YAXIS);
235         }
236         else if(joint->channels[i] == Channel::ZROTATION)
237         {
238           float radian;
239           stream >> radian;
240           rotation[2] = Quaternion(Radian(Degree(radian)), Vector3::ZAXIS);
241         }
242       }
243       joint->translations.push_back(translation);
244       joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
245     }
246   }
247 }
248
249 bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
250 {
251   std::ifstream file(path.data());
252   if(!file.is_open())
253   {
254     return false;
255   }
256
257   std::string line;
258   while(getline(file, line))
259   {
260     trim(line);
261     std::istringstream stream(line);
262     std::string        token;
263     std::getline(stream, token, ' ');
264     if(token == TOKEN_HIERARCHY.data())
265     {
266       std::string line;
267       while(getline(file, line))
268       {
269         trim(line);
270         std::istringstream stream(line);
271         std::string        token;
272         std::getline(stream, token, ' ');
273         if(token == TOKEN_ROOT.data())
274         {
275           std::getline(stream, token, ' ');
276           rootJoint->name = token;
277           ParseHierarchy(file, rootJoint);
278           break;
279         }
280       }
281     }
282     if(token == TOKEN_MOTION.data())
283     {
284       ParseMotion(file, rootJoint, frameCount, frameTime);
285     }
286   }
287   return true;
288 }
289
290 AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale)
291 {
292   AnimationDefinition animationDefinition;
293
294   animationDefinition.mName     = animationName;
295   animationDefinition.mDuration = frameTime * (frameCount - 1);
296   float keyFrameInterval        = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
297
298   std::vector<std::shared_ptr<Joint>> jointList;
299   MakeList(hierarchy, jointList);
300
301   if(!jointList.empty())
302   {
303     animationDefinition.mProperties.resize(jointList.size() * 2u); // translation and rotation
304     for(uint32_t i = 0; i < jointList.size(); ++i)
305     {
306       AnimatedProperty& translationProperty = animationDefinition.mProperties[i * 2u];
307       translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
308       translationProperty.mNodeName         = jointList[i]->name;
309       translationProperty.mPropertyName     = PROPERTY_NAME_POSITION.data();
310
311       AnimatedProperty& rotationProperty = animationDefinition.mProperties[i * 2u + 1];
312       rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
313       rotationProperty.mNodeName         = jointList[i]->name;
314       rotationProperty.mPropertyName     = PROPERTY_NAME_ORIENTATION.data();
315
316       translationProperty.mKeyFrames = Dali::KeyFrames::New();
317       rotationProperty.mKeyFrames    = Dali::KeyFrames::New();
318       for(uint32_t j = 0; j < frameCount; ++j)
319       {
320         translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] / scale));
321         rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
322       }
323     }
324   }
325
326   return animationDefinition;
327 }
328 } // namespace
329
330 AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
331 {
332   uint32_t               frameCount = 0;
333   float                  frameTime  = 0.0f;
334   std::shared_ptr<Joint> rootJoint(new Joint);
335   if(!ParseBvh(path, frameCount, frameTime, rootJoint))
336   {
337     AnimationDefinition animationDefinition;
338     return animationDefinition;
339   }
340
341   return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
342 }
343 } // namespace Loader
344 } // namespace Scene3D
345 } // namespace Dali