2 * Copyright (c) 2024 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/devel-api/animation/key-frames-devel.h>
24 #include <dali/integration-api/debug.h>
30 #include <string_view>
32 namespace Dali::Scene3D::Loader
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_OPENING_BRACE = "{";
54 static constexpr std::string_view TOKEN_CLOSING_BRACE = "}";
68 std::vector<float> values;
75 std::vector<Vector3> translations;
76 std::vector<Quaternion> rotations;
77 std::vector<Channel> channels;
78 std::vector<std::shared_ptr<Joint>> children;
81 void trim(std::string& s)
83 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
84 return !std::isspace(ch);
86 s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
87 return !std::isspace(ch);
92 bool ParseHierarchy(std::istream& file, std::shared_ptr<Joint>& joint)
95 bool braceExist = false;
96 while(std::getline(file, line))
99 std::istringstream stream(line);
101 std::getline(stream, token, ' ');
103 if(token == TOKEN_OFFSET.data())
105 stream >> joint->offset.x >> joint->offset.y >> joint->offset.z;
107 else if(token == TOKEN_CHANNELS.data())
109 uint32_t channelCount = 0;
110 std::getline(stream, token, ' ');
111 channelCount = static_cast<uint32_t>(std::atoi(token.c_str()));
112 for(uint32_t i = 0; i < channelCount; ++i)
114 std::getline(stream, token, ' ');
116 if(token == TOKEN_XPOSITION.data())
118 joint->channels.push_back(Channel::XPOSITION);
120 else if(token == TOKEN_YPOSITION.data())
122 joint->channels.push_back(Channel::YPOSITION);
124 else if(token == TOKEN_ZPOSITION.data())
126 joint->channels.push_back(Channel::ZPOSITION);
128 else if(token == TOKEN_XROTATION.data())
130 joint->channels.push_back(Channel::XROTATION);
132 else if(token == TOKEN_YROTATION.data())
134 joint->channels.push_back(Channel::YROTATION);
136 else if(token == TOKEN_ZROTATION.data())
138 joint->channels.push_back(Channel::ZROTATION);
142 else if(token == TOKEN_JOINT.data())
144 std::shared_ptr<Joint> child(new Joint);
145 joint->children.push_back(child);
146 std::getline(stream, token, ' ');
149 if(DALI_UNLIKELY(!ParseHierarchy(file, child)))
154 else if(line == TOKEN_END_SITE.data())
156 bool braceExistEndSite = false;
157 while(std::getline(file, line))
160 if(line == TOKEN_OPENING_BRACE.data())
162 if(DALI_UNLIKELY(braceExistEndSite))
164 DALI_LOG_ERROR("Parsing error : Joint[%s] End Site opening brace not matched\n", joint->name.c_str());
167 braceExistEndSite = true;
169 else if(line == TOKEN_CLOSING_BRACE.data())
171 if(DALI_UNLIKELY(!braceExistEndSite))
173 DALI_LOG_ERROR("Parsing error : Joint[%s] End Site closing brace not matched\n", joint->name.c_str());
179 if(DALI_UNLIKELY(!braceExistEndSite))
181 DALI_LOG_ERROR("Parsing error : Joint[%s] End Site opening brace not exist\n", joint->name.c_str());
185 else if(token == TOKEN_OPENING_BRACE.data())
187 if(DALI_UNLIKELY(braceExist))
189 DALI_LOG_ERROR("Parsing error : Joint[%s] opening brace not matched\n", joint->name.c_str());
194 else if(token == TOKEN_CLOSING_BRACE.data())
196 if(DALI_UNLIKELY(!braceExist))
198 DALI_LOG_ERROR("Parsing error : Joint[%s] closing brace not matched\n", joint->name.c_str());
204 if(DALI_UNLIKELY(!braceExist))
206 DALI_LOG_ERROR("Parsing error : Joint[%s] opening brace not exist\n", joint->name.c_str());
212 void MakeList(std::shared_ptr<Joint>& joint, std::vector<std::shared_ptr<Joint>>& jointList)
214 jointList.push_back(joint);
215 for(uint32_t i = 0; i < joint->children.size(); ++i)
217 MakeList(joint->children[i], jointList);
221 bool ParseMotion(std::istream& file, std::shared_ptr<Joint>& hierarchy, uint32_t& frameCount, float& frameTime)
223 std::vector<std::shared_ptr<Joint>> jointList;
224 MakeList(hierarchy, jointList);
226 bool frameCountLoaded = false;
227 bool frameTimeLoaded = false;
229 while((!frameCountLoaded || !frameTimeLoaded) && std::getline(file, line))
232 std::istringstream stream(line);
234 std::getline(stream, token, ':');
236 if(token == TOKEN_FRAMES.data())
238 stream >> frameCount;
239 frameCountLoaded = true;
241 else if(token == TOKEN_FRAME_TIME.data())
244 frameTimeLoaded = true;
248 if(DALI_UNLIKELY(!frameCountLoaded))
250 DALI_LOG_ERROR("Parsing error : Frames not exist!\n");
253 if(DALI_UNLIKELY(!frameTimeLoaded))
255 DALI_LOG_ERROR("Parsing error : Frame Time not exist!\n");
259 uint32_t loadedFrameCount = 0u;
261 while(std::getline(file, line))
264 if(DALI_UNLIKELY(line.empty()))
268 std::istringstream stream(line);
269 if(DALI_UNLIKELY(++loadedFrameCount > frameCount))
271 // Parse failed. Just skip decoding, and get the number of line for debug.
275 for(auto&& joint : jointList)
278 Quaternion rotation[3];
279 for(uint32_t i = 0; i < joint->channels.size(); ++i)
281 if(joint->channels[i] == Channel::XPOSITION)
283 stream >> translation.x;
285 else if(joint->channels[i] == Channel::YPOSITION)
287 stream >> translation.y;
289 else if(joint->channels[i] == Channel::ZPOSITION)
291 stream >> translation.z;
293 else if(joint->channels[i] == Channel::XROTATION)
297 rotation[0] = Quaternion(Radian(Degree(radian)), Vector3::XAXIS);
299 else if(joint->channels[i] == Channel::YROTATION)
303 rotation[1] = Quaternion(Radian(Degree(radian)), Vector3::YAXIS);
305 else if(joint->channels[i] == Channel::ZROTATION)
309 rotation[2] = Quaternion(Radian(Degree(radian)), Vector3::ZAXIS);
312 joint->translations.push_back(translation);
313 joint->rotations.push_back(rotation[2] * rotation[0] * rotation[1]);
317 if(DALI_UNLIKELY(loadedFrameCount != frameCount))
319 DALI_LOG_ERROR("Parsing error : Motion frame count not matched! expect : %u, loaded : %u\n", frameCount, loadedFrameCount);
326 bool ParseBvh(std::istream& file, uint32_t& frameCount, float& frameTime, std::shared_ptr<Joint>& rootJoint)
329 bool parseHierarchy = false;
330 bool parseMotion = false;
331 while(std::getline(file, line))
334 std::istringstream stream(line);
336 std::getline(stream, token, ' ');
337 if(token == TOKEN_HIERARCHY.data())
340 while(std::getline(file, line))
343 std::istringstream stream(line);
345 std::getline(stream, token, ' ');
346 if(token == TOKEN_ROOT.data())
348 std::getline(stream, token, ' ');
349 rootJoint->name = token;
350 parseHierarchy = ParseHierarchy(file, rootJoint);
355 if(token == TOKEN_MOTION.data())
357 parseMotion = ParseMotion(file, rootJoint, frameCount, frameTime);
360 return parseHierarchy && parseMotion;
363 AnimationDefinition GenerateAnimation(const std::string& animationName, std::shared_ptr<Joint>& hierarchy, uint32_t frameCount, float frameTime, bool useRootTranslationOnly, const Vector3& scale)
365 AnimationDefinition animationDefinition;
367 animationDefinition.SetName(animationName);
368 animationDefinition.SetDuration(frameTime * (frameCount - 1));
369 float keyFrameInterval = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
371 std::vector<std::shared_ptr<Joint>> jointList;
372 MakeList(hierarchy, jointList);
374 if(!jointList.empty())
376 uint32_t animationSize = jointList.size();
377 animationSize = (useRootTranslationOnly) ? (animationSize + 1u) : (animationSize * 2u);
378 animationDefinition.ReserveSize(animationSize);
379 uint32_t animationIndex = 0u;
380 for(uint32_t i = 0; i < jointList.size(); ++i)
382 AnimatedProperty translationProperty;
383 if(!useRootTranslationOnly || i == 0)
385 translationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());
386 translationProperty.mNodeName = jointList[i]->name;
387 translationProperty.mPropertyName = PROPERTY_NAME_POSITION.data();
390 AnimatedProperty rotationProperty;
391 rotationProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());
392 rotationProperty.mNodeName = jointList[i]->name;
393 rotationProperty.mPropertyName = PROPERTY_NAME_ORIENTATION.data();
395 translationProperty.mKeyFrames = Dali::KeyFrames::New();
396 rotationProperty.mKeyFrames = Dali::KeyFrames::New();
397 for(uint32_t j = 0; j < frameCount; ++j)
399 if(!useRootTranslationOnly || i == 0)
401 translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
403 rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
405 if(!useRootTranslationOnly || i == 0)
407 // Optimize keyframes, for heuristic!
408 DevelKeyFrames::OptimizeKeyFramesLinear(translationProperty.mKeyFrames);
409 animationDefinition.SetProperty(animationIndex++, std::move(translationProperty));
411 // Optimize keyframes, for heuristic!
412 DevelKeyFrames::OptimizeKeyFramesLinear(rotationProperty.mKeyFrames);
413 animationDefinition.SetProperty(animationIndex++, std::move(rotationProperty));
417 return animationDefinition;
420 AnimationDefinition LoadBvhInternal(std::istream& stream, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
422 uint32_t frameCount = 0;
423 float frameTime = 0.0f;
424 std::shared_ptr<Joint> rootJoint(new Joint);
426 if(!ParseBvh(stream, frameCount, frameTime, rootJoint))
428 AnimationDefinition animationDefinition;
429 return animationDefinition;
432 return GenerateAnimation(animationName, rootJoint, frameCount, frameTime, useRootTranslationOnly, scale);
436 AnimationDefinition LoadBvh(const std::string& path, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
438 Dali::FileStream fileStream(path);
439 std::iostream& stream = fileStream.GetStream();
443 DALI_LOG_ERROR("Fail to load bvh file : %s\n", path.c_str());
444 AnimationDefinition animationDefinition;
445 return animationDefinition;
448 return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
451 AnimationDefinition LoadBvhFromBuffer(const uint8_t* rawBuffer, int rawBufferLength, const std::string& animationName, bool useRootTranslationOnly, const Vector3& scale)
453 if(rawBuffer == nullptr || rawBufferLength == 0)
455 DALI_LOG_ERROR("Fail to load bvh buffer : buffer is empty!\n");
456 AnimationDefinition animationDefinition;
457 return animationDefinition;
460 Dali::FileStream fileStream(const_cast<uint8_t*>(rawBuffer), static_cast<size_t>(static_cast<uint32_t>(rawBufferLength)));
461 std::iostream& stream = fileStream.GetStream();
465 DALI_LOG_ERROR("Fail to load bvh buffer : buffer length : %d\n", rawBufferLength);
466 AnimationDefinition animationDefinition;
467 return animationDefinition;
470 return LoadBvhInternal(stream, animationName, useRootTranslationOnly, scale);
472 } // namespace Dali::Scene3D::Loader