1#include "scene/components/AnimationComponent.h"
4#include <unordered_set>
8#include "scene/GameObject.h"
14 std::unordered_set<std::string> seen;
15 seen.reserve(m_clips.size() + m_skelClips.size());
16 for (
const auto &kv : m_clips) { seen.insert(kv.first); }
17 for (
const auto &kv : m_skelClips) { seen.insert(kv.first); }
19 std::vector<std::string> out(seen.begin(), seen.end());
20 std::sort(out.begin(), out.end());
30 if (!m_clip && !m_activeSkelClip)
36 float duration = 0.0f;
37 if (m_clip) duration = std::max(duration, m_clip->
duration);
38 if (m_activeSkelClip) duration = std::max(duration, m_activeSkelClip->
duration);
41 if (duration > 0.0f && m_time > duration)
45 m_time = std::fmod(m_time, duration);
56 for (
auto &binding : m_bindings)
58 auto &obj = binding.first;
59 auto &trackIndices = binding.second->trackIndices;
60 for (
auto i : trackIndices)
62 auto &track = m_clip->
tracks[i];
63 if (!track.positions.empty())
65 obj->SetPosition(Interpolate(track.positions, m_time));
67 if (!track.rotations.empty())
69 obj->SetRotation(Interpolate(track.rotations, m_time));
71 if (!track.scales.empty())
73 obj->SetScale(Interpolate(track.scales, m_time));
80 if (m_activeSkelClip && m_skeleton)
82 EvaluatePose(*m_activeSkelClip, *m_skeleton, m_time, m_pose);
102 m_skelClips[name] = clip;
107 m_skeleton = skeleton;
114 auto nodeIt = m_clips.find(name);
115 if (nodeIt != m_clips.end())
125 auto skelIt = m_skelClips.find(name);
126 if (skelIt != m_skelClips.end())
128 m_activeSkelClip = skelIt->second.get();
136 m_activeSkelClip =
nullptr;
149void AnimationComponent::BuildBindings()
157 for (
size_t i = 0; i < m_clip->
tracks.size(); ++i)
159 auto &track = m_clip->
tracks[i];
163 auto it = m_bindings.find(targetObject);
164 if (it != m_bindings.end())
166 it->second->trackIndices.push_back(i);
169 auto binding = std::make_unique<ObjectBinding>();
170 binding->object = targetObject;
171 binding->trackIndices.push_back(i);
172 m_bindings.emplace(targetObject, std::move(binding));
178glm::vec3 AnimationComponent::Interpolate(
const std::vector<KeyFrameVec3> &keys,
float time)
182 return glm::vec3(0.0f);
185 if (keys.size() == 1)
187 return keys[0].value;
190 if (time <= keys.front().time)
192 return keys.front().value;
195 if (time >= keys.back().time)
197 return keys.back().value;
203 for (
size_t i = 1; i < keys.size(); ++i)
205 if (time <= keys[i].time)
212 i0 = i1 > 0 ? i1 - 1 : 0;
214 if (time >= keys[i0].time && time <= keys[i1].time)
216 float deltaTime = keys[i1].time - keys[i0].time;
217 float k = (time - keys[i0].time) / deltaTime;
219 return glm::mix(keys[i0].value, keys[i1].value, k);
222 return keys.back().value;
225glm::quat AnimationComponent::Interpolate(
const std::vector<KeyFrameQuat> &keys,
float time)
229 return glm::vec3(0.0f);
232 if (keys.size() == 1)
234 return keys[0].value;
237 if (time <= keys.front().time)
239 return keys.front().value;
242 if (time >= keys.back().time)
244 return keys.back().value;
250 for (
size_t i = 1; i < keys.size(); ++i)
252 if (time <= keys[i].time)
259 i0 = i1 > 0 ? i1 - 1 : 0;
261 if (time >= keys[i0].time && time <= keys[i1].time)
263 float deltaTime = keys[i1].time - keys[i0].time;
264 float k = (time - keys[i0].time) / deltaTime;
266 return glm::slerp(keys[i0].value, keys[i1].value, k);
269 return keys.back().value;
Per-bone TRS keyframe tracks driving a Skeleton.
Bone hierarchy + bind/offset matrices for GPU-skinned meshes.
void SetClip(AnimationClip *clip)
Set the active clip directly (bypasses the name registry).
std::vector< std::string > GetClipNames() const
List every clip name registered on this component (node + skeletal).
void Play(const std::string &name, bool loop=true)
Start playing a registered clip by name.
void SetSkeleton(const std::shared_ptr< Skeleton > &skeleton)
Bind a skeleton to the component so skeletal clips can be evaluated.
void Update(float deltaTime) override
Advance playback time and write interpolated transforms to bound objects.
void RegisterSkeletalClip(const std::string &name, const std::shared_ptr< SkeletalAnimationClip > &clip)
Register a skeletal clip (per-bone TRS tracks) — used by SkinnedMeshComponent.
void RegisterClip(const std::string &name, const std::shared_ptr< AnimationClip > &clip)
Register a named clip so it can be started via Play().
GameObject * FindChildByName(const std::string &name)
Depth-first search for a child with the given name.
void ComputePalette(const Skeleton &skeleton, Pose &pose)
Concatenate parent transforms and apply offsetMatrix to produce the GPU palette.
void ResetPoseToBind(const Skeleton &skeleton, Pose &pose)
Resize pose buffers to match skeleton and reset to bind pose.
void EvaluatePose(const SkeletalAnimationClip &clip, const Skeleton &skeleton, f32 time, Pose &pose)
Sample clip at time and write each animated bone's TRS into pose.localTransforms.
A named, playable animation containing one or more transform tracks.
std::vector< TransformTrack > tracks
Per-node animation channels.
float duration
Total clip length in seconds.
std::vector< glm::mat4 > finalPalette
Skinning matrices: globalPose * offsetMatrix.