1#include "AnimationSystem.h"
5#include "Resources/Animation.h"
6#include "Resources/Skeleton.h"
7#include "Resources/Mesh.h"
9#include "Systems/Core/TransformSystem.h"
11#include "Components/Core/SceneComponent.h"
12#include "Components/Core/MeshComponent.h"
14#include "Services/Time.h"
15#include "Services/Variables.h"
17#include "Utilities/Parallel.h"
19#include "Foundation/Logging/Logger.h"
20#include "Foundation/ComponentModel/Entity.h"
22#include <glm/gtx/compatibility.hpp>
33void Cogs::Core::updateSkeletonPose(
Skeleton & skeleton)
35 if (skeleton.bones.empty())
return;
37 for (
auto & bone : skeleton.bones) {
38 bool isRoot = bone.parentBone == size_t(- 1);
41 bone.absolute = bone.relative;
43 auto & parent = skeleton.bones[bone.parentBone];
45 bone.absolute = parent.absolute * bone.relative;
50template<
typename T,
typename Collection>
51T evalTrack(Collection & keyFrames, T defaultValue,
float time)
53 if (keyFrames.empty())
return defaultValue;
57 for (; frame < keyFrames.size() - 1; ++frame) {
58 if (time < keyFrames[frame + 1].t)
break;
61 T key = keyFrames[frame].value;
63 size_t nextFrame = frame + 1;
64 if (nextFrame >= keyFrames.size()) {
68 T nextKey = keyFrames[nextFrame].value;
70 const float range = keyFrames[frame + 1].t - keyFrames[frame].t;
71 const float diff = time - keyFrames[frame].t;
73 float v = diff / range;
74 v = glm::max(0.0f, glm::min(v, 1.0f));
76 return glm::lerp(key, nextKey, v);
79template<
typename T,
typename Collection>
80T evalRotationTrack(Collection & keyFrames, T defaultValue,
float time)
82 if (keyFrames.empty())
return defaultValue;
86 for (; frame < keyFrames.size() - 1; ++frame) {
87 if (time < keyFrames[frame + 1].t)
break;
90 T key = keyFrames[frame].value;
92 size_t nextFrame = frame + 1;
93 if (nextFrame >= keyFrames.size()) {
97 T nextKey = keyFrames[nextFrame].value;
99 const float range = keyFrames[frame + 1].t - keyFrames[frame].t;
100 const float diff = time - keyFrames[frame].t;
102 float v = diff / range;
103 v = glm::max(0.0f, glm::min(v, 1.0f));
105 return glm::slerp(key, nextKey, v);
110 const auto numTracks = clip.tracks.size();
111 cache.tracks.resize(numTracks);
113 for (
size_t j = 0; j < numTracks; ++j) {
114 auto & track = clip.tracks[j];
116 if (track.boneIndex == uint32_t(-1))
continue;
118 Bone & bone = skeleton.bones[track.boneIndex];
119 const auto position = evalTrack(track.translations, bone.pos, time);
120 const auto scale = evalTrack(track.scales, bone.scale, time);
121 const auto rotation = evalRotationTrack(track.rotations, bone.rot, time);
123 bone.relative = glm::translate(position) * glm::mat4_cast(rotation) * glm::scale(scale);
131 for (
auto & child : sceneComponent->children) {
132 if (name == child->getName()) {
135 auto found = findEntityByName(name, child.get());
137 if (found)
return found;
146 if (!animationComponent.
animation)
return;
148 auto & animationData = getData(&animationComponent);
152 if (!animationData.skeleton) {
153 animationData.skeleton = std::make_unique<Skeleton>();
154 animationData.pose = std::make_unique<PoseData>();
157 if (animationData.skeleton->bones.empty()) {
158 *animationData.skeleton = animation->skeleton;
160 for (
size_t i = 0; i < kMaxBones; ++i) {
161 animationData.pose->transforms[i] = glm::mat4(1.0f);
165 if (!animationComponent.
master && animationComponent.
clipIndex != -1) {
166 int clipIndex = animationComponent.
clipIndex;
167 if (clipIndex < 0 ||
static_cast<size_t>(clipIndex) >= animation->clips.size()) {
168 LOG_WARNING(logger,
"Animation clip index %d out of bounds.", clipIndex);
171 auto & clip = animation->clips[clipIndex];
173 float time = (globalTime + animationComponent.
timeOffset) * animationComponent.
timeFactor * (clip.resolution ? clip.resolution : 1000.0f);
174 time = clip.duration != 0 ? std::fmod(time, clip.duration) : 0.0f;
176 auto & skeleton = *animationData.skeleton;
178 evalClip(skeleton, clip, animationData.cache, time);
179 updateSkeletonPose(skeleton);
181 animationData.transformCache.resize(skeleton.bones.size());
183 for (
size_t i = 0; i < skeleton.bones.size(); ++i) {
184 auto & b = skeleton.bones[i];
186 if (b.hasOffset && b.used) {
187 b.absolute = skeleton.bindPose * b.absolute * b.inverseBindPose;
191 if (animationData.root ==
nullptr) {
193 animationData.root = masterAnim->getContainer();
199 assert(animationData.root);
202 if (animationData.transformCache[i]) {
204 if (animationData.transformCache[i].generation != comp->getGeneration()) {
213 if (
auto e = findEntityByName(b.name, animationData.root); e) {
228 if (meshComponent && meshComponent->meshHandle) {
231 if (mesh->getPoseIndexes().size() > kMaxBones) {
232 LOG_WARNING(logger,
"Bones array too large.");
236 animationData.pose->numPoses = mesh->getPoseIndexes().size();
238 auto poseSkeleton = animationData.skeleton.get();
239 if (animationComponent.
master) {
241 poseSkeleton = getData(masterComponent).skeleton.get();
244 for (
size_t i = 0; i < mesh->getPoseIndexes().
size(); ++i) {
245 animationData.pose->transforms[i] = poseSkeleton->bones[mesh->getPoseIndexes()[i]].absolute;
252 const float globalTime =
static_cast<float>(context->
time->getAnimationTime());
254 const bool workParallel = context->
engine->workParallel();
257 auto task = Parallel::processComponents(
258 context, pool,
"AnimationSystem::updateMaster", [&](
AnimationComponent & animationComponent,
size_t ) {
259 if (!animationComponent.
master) {
260 update(animationComponent, globalTime);
266 task = Parallel::processComponents(
267 context, pool,
"AnimationSystem::update", [&](
AnimationComponent & animationComponent,
size_t ) {
268 if (animationComponent.
master) {
269 update(animationComponent, globalTime);
275 for (
auto & animationComponent : pool) {
276 update(animationComponent, globalTime);
Base class for Component instances.
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
ComponentType * getComponent() const
class Entity * getContainer() const
Get the container currently owning this component instance.
ComponentHandle getComponentHandle() const
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
void update()
Updates the system state to that of the current frame.
size_t size()
Returns the number of active components.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Time > time
Time service instance.
std::unique_ptr< class Engine > engine
Engine instance.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Contains information on how the entity behaves in the scene.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
COGSFOUNDATION_API class Component * resolve() const
Resolve the handle, returning a pointer to the held Component instance.
static ComponentHandle Empty()
Returns an empty, invalid handle. Will evaluate to false if tested against using operator bool().
ComponentType * resolveComponent() const
Component handling animation of multiple Poses of an Animation Resource.
float timeOffset
Animation time offset in seconds.
int clipIndex
Active index of the Animation Clips. -1 = freeze Animation.
AnimationHandle animation
Animation resource handle.
ComponentModel::ComponentHandle master
Internal handle of Master when animating. Handled by AnimationSystem.
float timeFactor
Animation Time Scale factor.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.