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>
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
23 #include <dali/integration-api/debug.h>
29 #include <string_view>
31 namespace Dali::Scene3D::Loader
35 static constexpr std::string_view TOKEN_OFFSET = "OFFSET";
36 static constexpr std::string_view TOKEN_CHANNELS = "CHANNELS";
37 static constexpr std::string_view TOKEN_XPOSITION = "Xposition";
38 static constexpr std::string_view TOKEN_YPOSITION = "Yposition";
39 static constexpr std::string_view TOKEN_ZPOSITION = "Zposition";
40 static constexpr std::string_view TOKEN_XROTATION = "Xrotation";
41 static constexpr std::string_view TOKEN_YROTATION = "Yrotation";
42 static constexpr std::string_view TOKEN_ZROTATION = "Zrotation";
43 static constexpr std::string_view TOKEN_JOINT = "JOINT";
44 static constexpr std::string_view TOKEN_END_SITE = "End Site";
45 static constexpr std::string_view TOKEN_FRAMES = "Frames";
46 static constexpr std::string_view TOKEN_FRAME_TIME = "Frame Time";
47 static constexpr std::string_view TOKEN_HIERARCHY = "HIERARCHY";
48 static constexpr std::string_view TOKEN_ROOT = "ROOT";
49 static constexpr std::string_view TOKEN_MOTION = "MOTION";
50 static constexpr std::string_view PROPERTY_NAME_POSITION = "position";
51 static constexpr std::string_view PROPERTY_NAME_ORIENTATION = "orientation";
52 static constexpr std::string_view TOKEN_CLOSING_BRACE = "}";
66 std::vector<float> values;
73 std::vector<Vector3> translations;
74 std::vector<Quaternion> rotations;
75 std::vector<Channel> channels;
76 std::vector<std::shared_ptr<Joint>> children;
79 void trim(std::string& s)
81 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
82 return !std::isspace(ch);
84 s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
85 return !std::isspace(ch);
90 void ParseHierarchy(std::istream& file, std::shared_ptr<Joint>& joint)
93 while(std::getline(file, line))
96 std::istringstream stream(line);
98 std::getline(stream, token, ' ');
100 if(token == TOKEN_OFFSET.data())
102 stream >> joint->offset.x >> joint->offset.y >> joint->offset.z;
104 else if(token == TOKEN_CHANNELS.data())
106 uint32_t channelCount = 0;
107 std::getline(stream, token, ' ');
108 channelCount = static_cast<uint32_t>(std::atoi(token.c_str()));
109 for(uint32_t i = 0; i < channelCount; ++i)
111 std::getline(stream, token, ' ');
113 if(token == TOKEN_XPOSITION.data())
115 joint->channels.push_back(Channel::XPOSITION);
117 else if(token == TOKEN_YPOSITION.data())
119 joint->channels.push_back(Channel::YPOSITION);
121 else if(token == TOKEN_ZPOSITION.data())
123 joint->channels.push_back(Channel::ZPOSITION);
125 else if(token == TOKEN_XROTATION.data())
127 joint->channels.push_back(Channel::XROTATION);
129 else if(token == TOKEN_YROTATION.data())
131 joint->channels.push_back(Channel::YROTATION);
133 else if(token == TOKEN_ZROTATION.data())
135 joint->channels.push_back(Channel::ZROTATION);
139 else if(token == TOKEN_JOINT.data())
141 std::shared_ptr<Joint> child(new Joint);
142 joint->children.push_back(child);
143 std::getline(stream, token, ' ');
145 ParseHierarchy(file, child);
147 else if(line == TOKEN_END_SITE.data())
149 while(std::getline(file, line))
152 if(line == TOKEN_CLOSING_BRACE.data())
158 else if(token == TOKEN_CLOSING_BRACE.data())
165 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
167 jointList.push_back(joint);
168 for(uint32_t i = 0; i < joint->children.size(); ++i)
170 MakeList(joint->children[i], jointList);
174 void ParseMotion(std::istream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
176 std::vector<std::shared_ptr<Joint>> jointList;
177 MakeList(hierarchy, jointList);
179 bool frameCountLoaded = false;
180 bool frameTimeLoaded = false;
182 while((!frameCountLoaded || !frameTimeLoaded) && std::getline(file, line))
185 std::istringstream stream(line);
187 std::getline(stream, token, ':');
189 if(token == TOKEN_FRAMES.data())
191 stream >> frameCount;
192 frameCountLoaded = true;
194 else if(token == TOKEN_FRAME_TIME.data())
197 frameTimeLoaded = true;
201 while(std::getline(file, line))
204 std::istringstream stream(line);
205 for(auto&& joint : jointList)
208 Quaternion rotation[3];
209 for(uint32_t i = 0; i < joint->channels.size(); ++i)
211 if(joint->channels[i] == Channel::XPOSITION)
213 stream >> translation.x;
215 else if(joint->channels[i] == Channel::YPOSITION)
217 stream >> translation.y;
219 else if(joint->channels[i] == Channel::ZPOSITION)
221 stream >> translation.z;
223 else if(joint->channels[i] == Channel::XROTATION)
227 rotation[0] = Quaternion(Radian(Degree(radian)), Vector3::XAXIS);
229 else if(joint->channels[i] == Channel::YROTATION)
233 rotation[1] = Quaternion(Radian(Degree(radian)), Vector3::YAXIS);
235 else if(joint->channels[i] == Channel::ZROTATION)
239 rotation[2] = Quaternion(Radian(Degree(radian)), Vector3::ZAXIS);
242 joint->translations.push_back(translation);
243 joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
248 bool ParseBvh(std::istream& file, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
251 while(std::getline(file, line))
254 std::istringstream stream(line);
256 std::getline(stream, token, ' ');
257 if(token == TOKEN_HIERARCHY.data())
260 while(std::getline(file, line))
263 std::istringstream stream(line);
265 std::getline(stream, token, ' ');
266 if(token == TOKEN_ROOT.data())
268 std::getline(stream, token, ' ');
269 rootJoint->name = token;
270 ParseHierarchy(file, rootJoint);
275 if(token == TOKEN_MOTION.data())
277 ParseMotion(file, rootJoint, frameCount, frameTime);
283 AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, const Vector3& scale)
285 AnimationDefinition animationDefinition;
287 animationDefinition.SetName(animationName);
288 animationDefinition.SetDuration(frameTime * (frameCount - 1));
289 float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
291 std::vector<std::shared_ptr<Joint>> jointList;
292 MakeList(hierarchy, jointList);
294 if(!jointList.empty())
296 animationDefinition.ReserveSize(jointList.size() * 2u); // translation and rotation
297 for(uint32_t i = 0; i < jointList.size(); ++i)
299 AnimatedProperty translationProperty;
300 translationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());
301 translationProperty.mNodeName = jointList[i]->name;
302 translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
304 AnimatedProperty rotationProperty;
305 rotationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());
306 rotationProperty.mNodeName = jointList[i]->name;
307 rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
309 translationProperty.mKeyFrames = Dali::KeyFrames::New();
310 rotationProperty.mKeyFrames = Dali::KeyFrames::New();
311 for(uint32_t j = 0; j < frameCount; ++j)
313 translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
314 rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
316 animationDefinition.SetProperty(i * 2u, std::move(translationProperty));
317 animationDefinition.SetProperty(i * 2u + 1, std::move(rotationProperty));
321 return animationDefinition;
324 AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& animationName, const Vector3& scale)
326 uint32_t frameCount = 0;
327 float frameTime = 0.0f;
328 std::shared_ptr<Joint> rootJoint(new Joint);
330 if(!ParseBvh(stream, frameCount, frameTime, rootJoint))
332 AnimationDefinition animationDefinition;
333 return animationDefinition;
336 return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, scale);
340 AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, const Vector3& scale)
342 Dali::FileStream fileStream(path);
343 std::iostream& stream = fileStream.GetStream();
347 DALI_LOG_ERROR("Fail to load bvh file : %s\n", path.c_str());
348 AnimationDefinition animationDefinition;
349 return animationDefinition;
352 return LoadBvhInternal(stream, animationName, scale);
355 AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, const Vector3& scale)
357 if(rawBuffer == nullptr || rawBufferLength == 0)
359 DALI_LOG_ERROR("Fail to load bvh buffer : buffer is empty!\n");
360 AnimationDefinition animationDefinition;
361 return animationDefinition;
364 Dali::FileStream fileStream(const_cast<uint8_t*>(rawBuffer), static_cast<size_t>(static_cast<uint32_t>(rawBufferLength)));
365 std::iostream& stream = fileStream.GetStream();
369 DALI_LOG_ERROR("Fail to load bvh buffer : buffer length : %d\n", rawBufferLength);
370 AnimationDefinition animationDefinition;
371 return animationDefinition;
374 return LoadBvhInternal(stream, animationName, scale);
376 } // namespace Dali::Scene3D::Loader