Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
AnimationEvaluator.cpp
Go to the documentation of this file.
1
2
3#include "config.h"
4
8
11
12#include <assimp/anim.h>
13
14namespace Divide {
15
16// ------------------------------------------------------------------------------------------------
17// Constructor on a given animation.
18AnimEvaluator::AnimEvaluator(const aiAnimation* pAnim, U32 idx) noexcept
19{
20 _lastTime = 0.0;
21 ticksPerSecond(!IS_ZERO(pAnim->mTicksPerSecond)
22 ? pAnim->mTicksPerSecond
24
25 duration(pAnim->mDuration);
26 name(pAnim->mName.length > 0 ? pAnim->mName.data : Util::StringFormat("unnamed_anim_{}", idx));
27
28 Console::d_printfn(LOCALE_STR("CREATE_ANIMATION_BEGIN"), name().c_str());
29
30 _channels.resize(pAnim->mNumChannels);
31 for (U32 a = 0; a < pAnim->mNumChannels; a++) {
32 const aiNodeAnim* srcChannel = pAnim->mChannels[a];
33 AnimationChannel& dstChannel = _channels[a];
34
35 dstChannel._name = srcChannel->mNodeName.data;
36 dstChannel._nameKey = _ID(dstChannel._name.c_str());
37
38 for (U32 i(0); i < srcChannel->mNumPositionKeys; i++) {
39 dstChannel._positionKeys.push_back(srcChannel->mPositionKeys[i]);
40 }
41 for (U32 i(0); i < srcChannel->mNumRotationKeys; i++) {
42 dstChannel._rotationKeys.push_back(srcChannel->mRotationKeys[i]);
43 }
44 for (U32 i(0); i < srcChannel->mNumScalingKeys; i++) {
45 dstChannel._scalingKeys.push_back(srcChannel->mScalingKeys[i]);
46 }
47 dstChannel._numPositionKeys = srcChannel->mNumPositionKeys;
48 dstChannel._numRotationKeys = srcChannel->mNumRotationKeys;
49 dstChannel._numScalingKeys = srcChannel->mNumScalingKeys;
50 }
51
52 _lastPositions.resize(pAnim->mNumChannels, vec3<U32>());
53
54 Console::d_printfn(LOCALE_STR("CREATE_ANIMATION_END"), _name.c_str());
55}
56
58{
59 DIVIDE_ASSERT(boneBuffer() == nullptr && !_transforms.empty(),
60 "AnimEvaluator error: can't create bone buffer at current stage!");
61
63 "AnimEvaluator error: Too many bones for current node! "
64 "Increase MAX_BONE_COUNT_PER_NODE in Config!");
65
66 vector<mat4<F32>> animationData;
67
68 animationData.reserve( frameCount() * _transforms.size());
69 for (const auto& transform : _transforms)
70 {
71 for (const mat4<F32>& mat : transform.matrices())
72 {
73 animationData.push_back(mat);
74 }
75 }
76
77 if (!animationData.empty())
78 {
79 ShaderBufferDescriptor bufferDescriptor{};
80 bufferDescriptor._ringBufferLength = 1;
81 Util::StringFormat( bufferDescriptor._name, "BONE_BUFFER_{}", name() );
82 bufferDescriptor._bufferParams._elementCount = to_U32(animationData.size());
83 bufferDescriptor._bufferParams._elementSize = sizeof(mat4<F32>);
84 bufferDescriptor._bufferParams._flags._usageType = BufferUsageType::UNBOUND_BUFFER;
85 bufferDescriptor._bufferParams._flags._updateFrequency = BufferUpdateFrequency::ONCE;
86 bufferDescriptor._bufferParams._flags._updateUsage = BufferUpdateUsage::CPU_TO_GPU;
87 bufferDescriptor._initialData = { animationData.data(), animationData.size() * sizeof(mat4<F32>) };
88
89 _boneBuffer = context.newSB(bufferDescriptor);
90 return true;
91 }
92
93 return false;
94}
95
97{
98 D64 time = 0.0;
99
100 if (duration() > 0.0)
101 {
102 // get a [0.f ... 1.f) value by allowing the percent to wrap around 1
103 time = std::fmod(elapsedTimeS * ticksPerSecond(), duration());
104 }
105
106 const D64 percent = time / duration();
107
108 FrameIndex ret = {};
109 if (!_transforms.empty())
110 {
111 // this will invert the percent so the animation plays backwards
112 if (playAnimationForward())
113 {
114 ret._curr = std::min(to_I32(_transforms.size() * percent), to_I32(_transforms.size() - 1));
115 ret._prev = ret._curr > 0 ? ret._curr - 1 : to_I32(_transforms.size()) - 1;
116 ret._next = to_I32((ret._curr + 1) % _transforms.size());
117 }
118 else
119 {
120 ret._curr = std::min(to_I32(_transforms.size() * ((percent - 1.0f) * -1.0f)), to_I32(_transforms.size() - 1));
121 ret._prev = to_I32((ret._curr + 1) % _transforms.size());
122 ret._next = ret._curr > 0 ? ret._curr - 1 : to_I32(_transforms.size()) - 1;
123 }
124 }
125 return ret;
126}
127
128// ------------------------------------------------------------------------------------------------
129// Evaluates the animation tracks for a given time stamp.
130void AnimEvaluator::evaluate(const D64 dt, Bone* skeleton)
131{
132 const D64 pTime = dt * ticksPerSecond();
133
134 D64 time = 0.0f;
135 if (duration() > 0.0) {
136 time = std::fmod(pTime, duration());
137 }
138
139 const aiQuaternion presentRotationDefault(1, 0, 0, 0);
140
141 aiVector3D presentPosition(0, 0, 0);
142 aiQuaternion presentRotation(1, 0, 0, 0);
143 aiVector3D presentScaling(1, 1, 1);
144
145 // calculate the transformations for each animation channel
146 for (U32 a = 0; a < _channels.size(); a++) {
147
149 Bone* bonenode = skeleton->find(channel->_nameKey);
150
151 if (bonenode == nullptr) {
152 Console::d_errorfn(LOCALE_STR("ERROR_BONE_FIND"), channel->_name.c_str());
153 continue;
154 }
155
156 // ******** Position *****
157 if (!channel->_positionKeys.empty()) {
158 // Look for present frame number. Search from last position if time
159 // is after the last time, else from beginning
160 // Should be much quicker than always looking from start for the
161 // average use case.
162 U32 frame = time >= _lastTime ? _lastPositions[a].x : 0;
163 while (frame < channel->_positionKeys.size() - 1) {
164 if (time < channel->_positionKeys[frame + 1].mTime) {
165 break;
166 }
167 frame++;
168 }
169
170 // interpolate between this frame's value and next frame's value
171 const U32 nextFrame = (frame + 1) % channel->_positionKeys.size();
172
173 const aiVectorKey& key = channel->_positionKeys[frame];
174 const aiVectorKey& nextKey = channel->_positionKeys[nextFrame];
175 D64 diffTime = nextKey.mTime - key.mTime;
176 if (diffTime < 0.0) diffTime += duration();
177 if (diffTime > 0) {
178 const F32 factor = to_F32((time - key.mTime) / diffTime);
179 presentPosition =
180 key.mValue + (nextKey.mValue - key.mValue) * factor;
181 } else {
182 presentPosition = key.mValue;
183 }
184 _lastPositions[a].x = frame;
185 } else {
186 presentPosition.Set(0.0f, 0.0f, 0.0f);
187 }
188
189 // ******** Rotation *********
190 if (!channel->_rotationKeys.empty()) {
191 U32 frame = time >= _lastTime ? _lastPositions[a].y : 0;
192 while (frame < channel->_rotationKeys.size() - 1) {
193 if (time < channel->_rotationKeys[frame + 1].mTime) break;
194 frame++;
195 }
196
197 // interpolate between this frame's value and next frame's value
198 const U32 nextFrame = (frame + 1) % channel->_rotationKeys.size();
199
200 const aiQuatKey& key = channel->_rotationKeys[frame];
201 const aiQuatKey& nextKey = channel->_rotationKeys[nextFrame];
202 D64 diffTime = nextKey.mTime - key.mTime;
203 if (diffTime < 0.0) diffTime += duration();
204 if (diffTime > 0) {
205 const F32 factor = to_F32((time - key.mTime) / diffTime);
206 presentRotation = presentRotationDefault;
207 aiQuaternion::Interpolate(presentRotation, key.mValue,
208 nextKey.mValue, factor);
209 } else {
210 presentRotation = key.mValue;
211 }
212 _lastPositions[a].y = frame;
213 } else {
214 presentRotation = presentRotationDefault;
215 }
216
217 // ******** Scaling **********
218 if (!channel->_scalingKeys.empty()) {
219 U32 frame = time >= _lastTime ? _lastPositions[a].z : 0;
220 while (frame < channel->_scalingKeys.size() - 1) {
221 if (time < channel->_scalingKeys[frame + 1].mTime) break;
222 frame++;
223 }
224
225 presentScaling = channel->_scalingKeys[frame].mValue;
226 _lastPositions[a].z = frame;
227 } else {
228 presentScaling.Set(1.0f, 1.0f, 1.0f);
229 }
230
231 aiMatrix4x4 mat(presentRotation.GetMatrix());
232 mat.a1 *= presentScaling.x;
233 mat.b1 *= presentScaling.x;
234 mat.c1 *= presentScaling.x;
235 mat.a2 *= presentScaling.y;
236 mat.b2 *= presentScaling.y;
237 mat.c2 *= presentScaling.y;
238 mat.a3 *= presentScaling.z;
239 mat.b3 *= presentScaling.z;
240 mat.c3 *= presentScaling.z;
241 mat.a4 = presentPosition.x;
242 mat.b4 = presentPosition.y;
243 mat.c4 = presentPosition.z;
244 mat4<F32> out;
246 bonenode->localTransform(out);
247 }
248 _lastTime = time;
249}
250
251} //namespace Divide
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_ASSERT(...)
bool initBuffers(GFXDevice &context)
FrameIndex frameIndexAt(D64 elapsedTimeS) const noexcept
ShaderBuffer_uptr _boneBuffer
GPU buffer to hold bone transforms.
U32 frameCount() const noexcept
vector< AnimationChannel > _channels
vector that holds all bone channels
void evaluate(D64 dt, Bone *skeleton)
vector< BoneTransform > _transforms
Array to return transformations results inside.
vector< vec3< U32 > > _lastPositions
ShaderBuffer * boneBuffer() const
Bone * find(const string &name)
Definition: Bone.h:77
Rough around the edges Adapter pattern abstracting the actual rendering API and access to the GPU.
Definition: GFXDevice.h:215
ShaderBuffer_uptr newSB(const ShaderBufferDescriptor &descriptor)
Definition: GFXDevice.inl:214
void TransformMatrix(const aiMatrix4x4 &in, mat4< F32 > &out, const bool rowMajor) noexcept
constexpr U16 MAX_BONE_COUNT_PER_NODE
Maximum number of bones available per node.
Definition: config.h:117
Str StringFormat(const char *fmt, Args &&...args)
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
bool IS_ZERO(const T X) noexcept
constexpr U32 to_U32(const T value)
constexpr F32 to_F32(const T value)
eastl::vector< Type > vector
Definition: Vector.h:42
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
double D64
static constexpr F32 ANIMATION_TICKS_PER_SECOND
constexpr I32 to_I32(const T value)
uint32_t U32
vector< aiVectorKey > _positionKeys
vector< aiQuatKey > _rotationKeys
vector< aiVectorKey > _scalingKeys
static NO_INLINE void d_printfn(const char *format, T &&... args)
static NO_INLINE void d_errorfn(const char *format, T &&... args)