2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-scene3d/public-api/loader/bvh-loader.h>
26 #include <string_view>
28 namespace Dali::Scene3D::Loader
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 = "}";
63 std::vector<float> values;
70 std::vector<Vector3> translations;
71 std::vector<Quaternion> rotations;
72 std::vector<Channel> channels;
73 std::vector<std::shared_ptr<Joint>> children;
76 void trim(std::string& s)
78 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
79 return !std::isspace(ch);
81 s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
82 return !std::isspace(ch);
87 void ParseHierarchy(std::ifstream& file, std::shared_ptr<Joint>& joint)
90 while(std::getline(file, line))
93 std::istringstream stream(line);
95 std::getline(stream, token, ' ');
97 if(token == TOKEN_OFFSET.data())
99 stream >> joint->offset.x >> joint->offset.y >> joint->offset.z;
101 else if(token == TOKEN_CHANNELS.data())
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)
108 std::getline(stream, token, ' ');
110 if(token == TOKEN_XPOSITION.data())
112 joint->channels.push_back(Channel::XPOSITION);
114 else if(token == TOKEN_YPOSITION.data())
116 joint->channels.push_back(Channel::YPOSITION);
118 else if(token == TOKEN_ZPOSITION.data())
120 joint->channels.push_back(Channel::ZPOSITION);
122 else if(token == TOKEN_XROTATION.data())
124 joint->channels.push_back(Channel::XROTATION);
126 else if(token == TOKEN_YROTATION.data())
128 joint->channels.push_back(Channel::YROTATION);
130 else if(token == TOKEN_ZROTATION.data())
132 joint->channels.push_back(Channel::ZROTATION);
136 else if(token == TOKEN_JOINT.data())
138 std::shared_ptr<Joint> child(new Joint);
139 joint->children.push_back(child);
140 std::getline(stream, token, ' ');
142 ParseHierarchy(file, child);
144 else if(line == TOKEN_END_SITE.data())
146 while(std::getline(file, line))
149 if(line == TOKEN_CLOSING_BRACE.data())
155 else if(token == TOKEN_CLOSING_BRACE.data())
162 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
164 jointList.push_back(joint);
165 for(uint32_t i = 0; i < joint->children.size(); ++i)
167 MakeList(joint->children[i], jointList);
171 void ParseMotion(std::ifstream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
173 std::vector<std::shared_ptr<Joint>> jointList;
174 MakeList(hierarchy, jointList);
176 bool frameCountLoaded = false;
177 bool frameTimeLoaded = false;
179 while((!frameCountLoaded || !frameTimeLoaded) && std::getline(file, line))
182 std::istringstream stream(line);
184 std::getline(stream, token, ':');
186 if(token == TOKEN_FRAMES.data())
188 stream >> frameCount;
189 frameCountLoaded = true;
191 else if(token == TOKEN_FRAME_TIME.data())
194 frameTimeLoaded = true;
198 while(getline(file, line))
201 std::istringstream stream(line);
202 for(auto&& joint : jointList)
205 Quaternion rotation[3];
206 for(uint32_t i = 0; i < joint->channels.size(); ++i)
208 if(joint->channels[i] == Channel::XPOSITION)
210 stream >> translation.x;
212 else if(joint->channels[i] == Channel::YPOSITION)
214 stream >> translation.y;
216 else if(joint->channels[i] == Channel::ZPOSITION)
218 stream >> translation.z;
220 else if(joint->channels[i] == Channel::XROTATION)
224 rotation[0] = Quaternion(Radian(Degree(radian)), Vector3::XAXIS);
226 else if(joint->channels[i] == Channel::YROTATION)
230 rotation[1] = Quaternion(Radian(Degree(radian)), Vector3::YAXIS);
232 else if(joint->channels[i] == Channel::ZROTATION)
236 rotation[2] = Quaternion(Radian(Degree(radian)), Vector3::ZAXIS);
239 joint->translations.push_back(translation);
240 joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
245 bool ParseBvh(const std::string& path, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
247 std::ifstream file(path.data());
254 while(getline(file, line))
257 std::istringstream stream(line);
259 std::getline(stream, token, ' ');
260 if(token == TOKEN_HIERARCHY.data())
263 while(getline(file, line))
266 std::istringstream stream(line);
268 std::getline(stream, token, ' ');
269 if(token == TOKEN_ROOT.data())
271 std::getline(stream, token, ' ');
272 rootJoint->name = token;
273 ParseHierarchy(file, rootJoint);
278 if(token == TOKEN_MOTION.data())
280 ParseMotion(file, rootJoint, frameCount, frameTime);
286 AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale)
288 AnimationDefinition animationDefinition;
290 animationDefinition.mName = animationName;
291 animationDefinition.mDuration = frameTime * (frameCount - 1);
292 float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
294 std::vector<std::shared_ptr<Joint>> jointList;
295 MakeList(hierarchy, jointList);
297 if(!jointList.empty())
299 animationDefinition.mProperties.resize(jointList.size() * 2u); // translation and rotation
300 for(uint32_t i = 0; i < jointList.size(); ++i)
302 AnimatedProperty& translationProperty = animationDefinition.mProperties[i * 2u];
303 translationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.mDuration);
304 translationProperty.mNodeName = jointList[i]->name;
305 translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
307 AnimatedProperty& rotationProperty = animationDefinition.mProperties[i * 2u + 1];
308 rotationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.mDuration);
309 rotationProperty.mNodeName = jointList[i]->name;
310 rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
312 translationProperty.mKeyFrames = Dali::KeyFrames::New();
313 rotationProperty.mKeyFrames = Dali::KeyFrames::New();
314 for(uint32_t j = 0; j < frameCount; ++j)
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]);
322 return animationDefinition;
326 AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
328 uint32_t frameCount = 0;
329 float frameTime = 0.0f;
330 std::shared_ptr<Joint> rootJoint(new Joint);
331 if(!ParseBvh(path, frameCount, frameTime, rootJoint))
333 AnimationDefinition animationDefinition;
334 return animationDefinition;
337 return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
339 } // namespace Dali::Scene3D::Loader