Change public member variable to private
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / bvh-loader.cpp
1 /*
2  * Copyright (c) 2023 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::Scene3D::Loader
29 {
30 namespace
31 {
32 static constexpr std::string_view TOKEN_OFFSET              = "OFFSET";
33 static constexpr std::string_view TOKEN_CHANNELS            = "CHANNELS";
34 static constexpr std::string_view TOKEN_XPOSITION           = "Xposition";
35 static constexpr std::string_view TOKEN_YPOSITION           = "Yposition";
36 static constexpr std::string_view TOKEN_ZPOSITION           = "Zposition";
37 static constexpr std::string_view TOKEN_XROTATION           = "Xrotation";
38 static constexpr std::string_view TOKEN_YROTATION           = "Yrotation";
39 static constexpr std::string_view TOKEN_ZROTATION           = "Zrotation";
40 static constexpr std::string_view TOKEN_JOINT               = "JOINT";
41 static constexpr std::string_view TOKEN_END_SITE            = "End Site";
42 static constexpr std::string_view TOKEN_FRAMES              = "Frames";
43 static constexpr std::string_view TOKEN_FRAME_TIME          = "Frame Time";
44 static constexpr std::string_view TOKEN_HIERARCHY           = "HIERARCHY";
45 static constexpr std::string_view TOKEN_ROOT                = "ROOT";
46 static constexpr std::string_view TOKEN_MOTION              = "MOTION";
47 static constexpr std::string_view PROPERTY_NAME_POSITION    = "position";
48 static constexpr std::string_view PROPERTY_NAME_ORIENTATION = "orientation";
49 static constexpr std::string_view TOKEN_CLOSING_BRACE       = "}";
50
51 enum class Channel
52 {
53   XPOSITION = 0,
54   YPOSITION,
55   ZPOSITION,
56   XROTATION,
57   YROTATION,
58   ZROTATION
59 };
60
61 struct Frame
62 {
63   std::vector<float> values;
64 };
65
66 struct Joint
67 {
68   std::string                         name;
69   Vector3                             offset;
70   std::vector<Vector3>                translations;
71   std::vector<Quaternion>             rotations;
72   std::vector<Channel>                channels;
73   std::vector<std::shared_ptr<Joint>> children;
74 };
75
76 void trim(std::string& s)
77 {
78   s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
79             return !std::isspace(ch);
80           }));
81   s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
82             return !std::isspace(ch);
83           }).base(),
84           s.end());
85 }
86
87 void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
88 {
89   std::string line;
90   while(std::getline(file, line))
91   {
92     trim(line);
93     std::istringstream stream(line);
94     std::string        token;
95     std::getline(stream, token, ' ');
96
97     if(token == TOKEN_OFFSET.data())
98     {
99       stream >> joint->offset.x >> joint->offset.y >> joint->offset.z;
100     }
101     else if(token == TOKEN_CHANNELS.data())
102     {
103       uint32_t channelCount = 0;
104       std::getline(stream, token, ' ');
105       channelCount = static_cast<uint32_t>(std::atoi(token.c_str()));
106       for(uint32_t i = 0; i < channelCount; ++i)
107       {
108         std::getline(stream, token, ' ');
109         trim(token);
110         if(token == TOKEN_XPOSITION.data())
111         {
112           joint->channels.push_back(Channel::XPOSITION);
113         }
114         else if(token == TOKEN_YPOSITION.data())
115         {
116           joint->channels.push_back(Channel::YPOSITION);
117         }
118         else if(token == TOKEN_ZPOSITION.data())
119         {
120           joint->channels.push_back(Channel::ZPOSITION);
121         }
122         else if(token == TOKEN_XROTATION.data())
123         {
124           joint->channels.push_back(Channel::XROTATION);
125         }
126         else if(token == TOKEN_YROTATION.data())
127         {
128           joint->channels.push_back(Channel::YROTATION);
129         }
130         else if(token == TOKEN_ZROTATION.data())
131         {
132           joint->channels.push_back(Channel::ZROTATION);
133         }
134       }
135     }
136     else if(token == TOKEN_JOINT.data())
137     {
138       std::shared_ptr<Joint> child(new Joint);
139       joint->children.push_back(child);
140       std::getline(stream, token, ' ');
141       child->name = token;
142       ParseHierarchy(file, child);
143     }
144     else if(line == TOKEN_END_SITE.data())
145     {
146       while(std::getline(file, line))
147       {
148         trim(line);
149         if(line == TOKEN_CLOSING_BRACE.data())
150         {
151           break;
152         }
153       }
154     }
155     else if(token == TOKEN_CLOSING_BRACE.data())
156     {
157       break;
158     }
159   }
160 }
161
162 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
163 {
164   jointList.push_back(joint);
165   for(uint32_t i = 0; i < joint->children.size(); ++i)
166   {
167     MakeList(joint->children[i], jointList);
168   }
169 }
170
171 void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
172 {
173   std::vector<std::shared_ptr<Joint>> jointList;
174   MakeList(hierarchy, jointList);
175
176   bool        frameCountLoaded = false;
177   bool        frameTimeLoaded  = false;
178   std::string line;
179   while((!frameCountLoaded || !frameTimeLoaded) && std::getline(file, line))
180   {
181     trim(line);
182     std::istringstream stream(line);
183     std::string        token;
184     std::getline(stream, token, ':');
185     trim(token);
186     if(token == TOKEN_FRAMES.data())
187     {
188       stream >> frameCount;
189       frameCountLoaded = true;
190     }
191     else if(token == TOKEN_FRAME_TIME.data())
192     {
193       stream >> frameTime;
194       frameTimeLoaded = true;
195     }
196   }
197
198   while(getline(file, line))
199   {
200     trim(line);
201     std::istringstream stream(line);
202     for(auto&& joint : jointList)
203     {
204       Vector3    translation;
205       Quaternion rotation[3];
206       for(uint32_t i = 0; i < joint->channels.size(); ++i)
207       {
208         if(joint->channels[i] == Channel::XPOSITION)
209         {
210           stream >> translation.x;
211         }
212         else if(joint->channels[i] == Channel::YPOSITION)
213         {
214           stream >> translation.y;
215         }
216         else if(joint->channels[i] == Channel::ZPOSITION)
217         {
218           stream >> translation.z;
219         }
220         else if(joint->channels[i] == Channel::XROTATION)
221         {
222           float radian;
223           stream >> radian;
224           rotation[0] = Quaternion(Radian(Degree(radian)), Vector3::XAXIS);
225         }
226         else if(joint->channels[i] == Channel::YROTATION)
227         {
228           float radian;
229           stream >> radian;
230           rotation[1] = Quaternion(Radian(Degree(radian)), Vector3::YAXIS);
231         }
232         else if(joint->channels[i] == Channel::ZROTATION)
233         {
234           float radian;
235           stream >> radian;
236           rotation[2] = Quaternion(Radian(Degree(radian)), Vector3::ZAXIS);
237         }
238       }
239       joint->translations.push_back(translation);
240       joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
241     }
242   }
243 }
244
245 bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
246 {
247   std::ifstream file(path.data());
248   if(!file.is_open())
249   {
250     return false;
251   }
252
253   std::string line;
254   while(getline(file, line))
255   {
256     trim(line);
257     std::istringstream stream(line);
258     std::string        token;
259     std::getline(stream, token, ' ');
260     if(token == TOKEN_HIERARCHY.data())
261     {
262       std::string line;
263       while(getline(file, line))
264       {
265         trim(line);
266         std::istringstream stream(line);
267         std::string        token;
268         std::getline(stream, token, ' ');
269         if(token == TOKEN_ROOT.data())
270         {
271           std::getline(stream, token, ' ');
272           rootJoint->name = token;
273           ParseHierarchy(file, rootJoint);
274           break;
275         }
276       }
277     }
278     if(token == TOKEN_MOTION.data())
279     {
280       ParseMotion(file, rootJoint, frameCount, frameTime);
281     }
282   }
283   return true;
284 }
285
286 AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale)
287 {
288   AnimationDefinition animationDefinition;
289
290   animationDefinition.SetName(animationName);
291   animationDefinition.SetDuration(frameTime * (frameCount - 1));
292   float keyFrameInterval        = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
293
294   std::vector<std::shared_ptr<Joint>> jointList;
295   MakeList(hierarchy, jointList);
296
297   if(!jointList.empty())
298   {
299     animationDefinition.ReserveSize(jointList.size() * 2u); // translation and rotation
300     for(uint32_t i = 0; i < jointList.size(); ++i)
301     {
302       AnimatedProperty translationProperty;
303       translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
304       translationProperty.mNodeName         = jointList[i]->name;
305       translationProperty.mPropertyName     = PROPERTY_NAME_POSITION.data();
306
307       AnimatedProperty rotationProperty;
308       rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
309       rotationProperty.mNodeName         = jointList[i]->name;
310       rotationProperty.mPropertyName     = PROPERTY_NAME_ORIENTATION.data();
311
312       translationProperty.mKeyFrames = Dali::KeyFrames::New();
313       rotationProperty.mKeyFrames    = Dali::KeyFrames::New();
314       for(uint32_t j = 0; j < frameCount; ++j)
315       {
316         translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
317         rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
318       }
319       animationDefinition.SetProperty(i * 2u, std::move(translationProperty));
320       animationDefinition.SetProperty(i * 2u + 1, std::move(rotationProperty));
321     }
322   }
323
324   return animationDefinition;
325 }
326 } // namespace
327
328 AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
329 {
330   uint32_t               frameCount = 0;
331   float                  frameTime  = 0.0f;
332   std::shared_ptr<Joint> rootJoint(new Joint);
333   if(!ParseBvh(path, frameCount, frameTime, rootJoint))
334   {
335     AnimationDefinition animationDefinition;
336     return animationDefinition;
337   }
338
339   return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
340 }
341 } // namespace Dali::Scene3D::Loader