2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
19 #include <dali-scene3d/public-api/loader/facial-animation-loader.h>
\r
21 // EXTERNAL INCLUDES
\r
22 #include <dali/devel-api/animation/key-frames-devel.h>
\r
25 // INTERNAL INCLUDES
\r
26 #include <dali-scene3d/internal/loader/json-reader.h>
\r
27 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
\r
28 #include <dali-scene3d/public-api/loader/utils.h>
\r
30 namespace js = json;
\r
36 const float MILLISECONDS_TO_SECONDS = 0.001f;
\r
40 std::vector<std::vector<float>> mKeys;
\r
41 std::string_view mNodeName;
\r
42 uint32_t mNumberOfMorphTarget;
\r
43 std::string_view mVersion;
\r
44 std::string_view mFullName;
\r
45 std::vector<std::string_view> mMorphNames;
\r
48 struct FacialAnimation
\r
50 std::string_view mName;
\r
51 std::vector<BlendShape> mBlendShapes;
\r
52 std::string_view mVersion;
\r
53 uint32_t mNumberOfShapes;
\r
54 std::vector<uint32_t> mTime;
\r
55 uint32_t mNumberOfFrames;
\r
58 std::vector<std::vector<float>> ReadBlendShapeKeys(const json_value_s& j)
\r
60 auto& jo = js::Cast<json_array_s>(j);
\r
61 std::vector<std::vector<float>> result;
\r
63 result.reserve(jo.length);
\r
68 result.push_back(std::move(js::Read::Array<float, js::Read::Number>(*i->value)));
\r
75 const js::Reader<BlendShape>& GetBlendShapeReader()
\r
77 static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()
\r
78 .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))
\r
79 .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))
\r
80 .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))
\r
81 .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))
\r
82 .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))
\r
83 .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));
\r
84 return BLEND_SHAPE_READER;
\r
87 const js::Reader<FacialAnimation>& GetFacialAnimationReader()
\r
89 static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()
\r
90 .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))
\r
91 .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))
\r
92 .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))
\r
93 .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))
\r
94 .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))
\r
95 .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));
\r
96 return FACIAL_ANIMATION_READER;
\r
99 Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::unique_ptr& root)
\r
101 static bool setObjectReaders = true;
\r
102 if(setObjectReaders)
\r
104 // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.
\r
105 js::SetObjectReader(GetBlendShapeReader());
\r
106 setObjectReaders = false;
\r
109 auto& rootObj = js::Cast<json_object_s>(*root);
\r
111 FacialAnimation facialAnimation;
\r
112 GetFacialAnimationReader().Read(rootObj, facialAnimation);
\r
114 Dali::Scene3D::Loader::AnimationDefinition animationDefinition;
\r
115 animationDefinition.SetName(facialAnimation.mName.data());
\r
116 animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));
\r
118 // Calculate the number of animated properties.
\r
119 uint32_t numberOfAnimatedProperties = 0u;
\r
120 for(const auto& blendShape : facialAnimation.mBlendShapes)
\r
122 numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;
\r
124 animationDefinition.ReserveSize(numberOfAnimatedProperties);
\r
126 uint32_t targets = 0u;
\r
127 for(const auto& blendShape : facialAnimation.mBlendShapes)
\r
129 for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)
\r
131 Dali::Scene3D::Loader::AnimatedProperty animatedProperty;
\r
132 animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());
\r
133 animatedProperty.mNodeName = blendShape.mNodeName;
\r
134 std::stringstream weightPropertyStream;
\r
135 weightPropertyStream << Dali::Scene3D::Loader::BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";
\r
136 animatedProperty.mPropertyName = weightPropertyStream.str();
\r
138 animatedProperty.mKeyFrames = Dali::KeyFrames::New();
\r
139 for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)
\r
141 const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();
\r
142 animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);
\r
144 // Optimize keyframes, for heuristic!
\r
145 DevelKeyFrames::OptimizeKeyFramesLinear(animatedProperty.mKeyFrames);
\r
146 animationDefinition.SetProperty(targets + morphTargetIndex, std::move(animatedProperty));
\r
149 targets += blendShape.mNumberOfMorphTarget;
\r
152 return animationDefinition;
\r
154 } // unnamed namespace
\r
160 AnimationDefinition LoadFacialAnimation(const std::string& url)
\r
162 bool failed = false;
\r
163 auto jsonData = LoadTextFile(url.c_str(), &failed);
\r
166 ExceptionFlinger(ASSERT_LOCATION) << "Failed to load " << url << ".";
\r
169 json::unique_ptr root(json_parse(jsonData.c_str(), jsonData.size()));
\r
172 ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json " << url << ".";
\r
175 return LoadFacialAnimationInternal(root);
\r
178 AnimationDefinition LoadFacialAnimationFromBuffer(const uint8_t* rawBuffer, int rawBufferLength)
\r
180 json::unique_ptr root(json_parse(rawBuffer, static_cast<size_t>(static_cast<uint32_t>(rawBufferLength))));
\r
183 ExceptionFlinger(ASSERT_LOCATION) << "Failed to parse json from buffer.";
\r
186 return LoadFacialAnimationInternal(root);
\r
188 } // namespace Loader
\r
189 } // namespace Scene3D
\r
190 } // namespace Dali
\r