From b996c43f5f9fc4be63c367ed57bb74ad7c22b529 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Thu, 21 Sep 2017 11:36:39 +0800 Subject: [PATCH] Add support for skeletal animations --- data | 2 +- lib/emergent | 2 +- src/game/ant.cpp | 25 ++++++++ src/game/ant.hpp | 1 + src/model-loader.cpp | 146 +++++++++++++++++++++++++++++++++++++------ src/model-loader.hpp | 26 +++++++- 6 files changed, 179 insertions(+), 23 deletions(-) diff --git a/data b/data index e0fb251..11f999a 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit e0fb2511f4d08bb4823535ebdaa79de3cf2caf59 +Subproject commit 11f999abf6d47962610f6c9696a8d4d4ee2a1bcb diff --git a/lib/emergent b/lib/emergent index 0d4b841..f07060a 160000 --- a/lib/emergent +++ b/lib/emergent @@ -1 +1 @@ -Subproject commit 0d4b84100382a6053dfd2da5e7707bc28477ac87 +Subproject commit f07060a2fd956170b046bf412fa3b9b73b06f320 diff --git a/src/game/ant.cpp b/src/game/ant.cpp index 3b42d01..06a5367 100644 --- a/src/game/ant.cpp +++ b/src/game/ant.cpp @@ -36,6 +36,8 @@ Ant::Ant(Colony* colony): modelInstance.setModel(colony->getAntModel()); modelInstance.setPose(pose); + + animationTime = 0.0f; } Ant::~Ant() @@ -45,6 +47,7 @@ Ant::~Ant() void Ant::rotateHead() { + /* const Bone* headBone = pose->getSkeleton()->getBone("left-flagellum"); if (headBone != nullptr) { @@ -56,6 +59,28 @@ void Ant::rotateHead() pose->setRelativeTransform(boneIndex, transform); pose->concatenate(); } + */ + + const Animation* animation = pose->getSkeleton()->getAnimation("eat"); + if (animation != nullptr) + { + for (std::size_t i = 0; i < animation->getChannelCount(); ++i) + { + const AnimationChannel* channel = animation->getChannel(i); + + std::size_t boneIndex = channel->getChannelID(); + Transform transform = channel->interpolateBoundingKeyFrames(animationTime); + + pose->setRelativeTransform(channel->getChannelID(), transform); + } + pose->concatenate(); + + animationTime += 0.5f; + if (animationTime > animation->getEndTime()) + { + animationTime = animation->getStartTime(); + } + } } void Ant::move(const Vector3& velocity) diff --git a/src/game/ant.hpp b/src/game/ant.hpp index b18ef71..12c43ac 100644 --- a/src/game/ant.hpp +++ b/src/game/ant.hpp @@ -96,6 +96,7 @@ private: Colony* colony; Ant::State state; + float animationTime; Transform transform; ModelInstance modelInstance; diff --git a/src/model-loader.cpp b/src/model-loader.cpp index 6cb58a5..9572090 100644 --- a/src/model-loader.cpp +++ b/src/model-loader.cpp @@ -174,35 +174,104 @@ Model* ModelLoader::load(const std::string& filename) { // Allocate skeleton data skeletonData = new SkeletonData(); + skeletonData->animations = nullptr; // Read bone count read16(&skeletonData->boneCount, &bufferOffset); // Allocate bones - skeletonData->boneData = new BoneData[skeletonData->boneCount]; + skeletonData->bones = new BoneData[skeletonData->boneCount]; // Read bones for (std::size_t i = 0; i < skeletonData->boneCount; ++i) { - BoneData* boneData = &skeletonData->boneData[i]; - boneData->children = nullptr; + BoneData* bone = &skeletonData->bones[i]; + bone->children = nullptr; - readString(&boneData->name, &bufferOffset); - read16(&boneData->parent, &bufferOffset); - read16(&boneData->childCount, &bufferOffset); - boneData->children = new std::uint16_t[boneData->childCount]; - for (std::size_t j = 0; j < boneData->childCount; ++j) + readString(&bone->name, &bufferOffset); + read16(&bone->parent, &bufferOffset); + read16(&bone->childCount, &bufferOffset); + bone->children = new std::uint16_t[bone->childCount]; + for (std::size_t j = 0; j < bone->childCount; ++j) { - read16(&boneData->children[j], &bufferOffset); + read16(&bone->children[j], &bufferOffset); + } + read32(&bone->translation.x, &bufferOffset); + read32(&bone->translation.y, &bufferOffset); + read32(&bone->translation.z, &bufferOffset); + read32(&bone->rotation.w, &bufferOffset); + read32(&bone->rotation.x, &bufferOffset); + read32(&bone->rotation.y, &bufferOffset); + read32(&bone->rotation.z, &bufferOffset); + read32(&bone->length, &bufferOffset); + } + + // Read animation count + read16(&skeletonData->animationCount, &bufferOffset); + + if (skeletonData->animationCount != 0) + { + // Allocate animations + skeletonData->animations = new AnimationData[skeletonData->animationCount]; + + // Read animations + for (std::size_t i = 0; i < skeletonData->animationCount; ++i) + { + AnimationData* animation = &skeletonData->animations[i]; + + // Read animation name + readString(&animation->name, &bufferOffset); + + // Read time frame + read32(&animation->startTime, &bufferOffset); + read32(&animation->endTime, &bufferOffset); + + // Read channel count + read16(&animation->channelCount, &bufferOffset); + + // Allocate channels + animation->channels = new ChannelData[animation->channelCount]; + + // Read channels + for (std::size_t j = 0; j < animation->channelCount; ++j) + { + ChannelData* channel = &animation->channels[j]; + + // Read channel ID + read16(&channel->id, &bufferOffset); + + // Read keyframe count + read16(&channel->keyFrameCount, &bufferOffset); + + // Allocate keyframes + channel->keyFrames = new KeyFrameData[channel->keyFrameCount]; + + // Read keyframes + for (std::size_t k = 0; k < channel->keyFrameCount; ++k) + { + KeyFrameData* keyFrame = &channel->keyFrames[k]; + + // Read keyframe time + read32(&keyFrame->time, &bufferOffset); + + // Read keyframe translation + read32(&keyFrame->transform.translation.x, &bufferOffset); + read32(&keyFrame->transform.translation.y, &bufferOffset); + read32(&keyFrame->transform.translation.z, &bufferOffset); + + // Read keyframe rotation + read32(&keyFrame->transform.rotation.w, &bufferOffset); + read32(&keyFrame->transform.rotation.x, &bufferOffset); + read32(&keyFrame->transform.rotation.y, &bufferOffset); + read32(&keyFrame->transform.rotation.z, &bufferOffset); + + // Read keyframe scale + read32(&keyFrame->transform.scale.x, &bufferOffset); + read32(&keyFrame->transform.scale.y, &bufferOffset); + read32(&keyFrame->transform.scale.z, &bufferOffset); + } + } } - read32(&boneData->translation.x, &bufferOffset); - read32(&boneData->translation.y, &bufferOffset); - read32(&boneData->translation.z, &bufferOffset); - read32(&boneData->rotation.w, &bufferOffset); - read32(&boneData->rotation.x, &bufferOffset); - read32(&boneData->rotation.y, &bufferOffset); - read32(&boneData->rotation.z, &bufferOffset); - read32(&boneData->length, &bufferOffset); } } @@ -339,11 +408,35 @@ Model* ModelLoader::load(const std::string& filename) Skeleton* skeleton = new Skeleton(); // Construct bone hierarchy from bone data - constructBoneHierarchy(skeleton->getRootBone(), skeletonData->boneData, 0); + constructBoneHierarchy(skeleton->getRootBone(), skeletonData->bones, 0); // Calculate bind pose skeleton->calculateBindPose(); + // Create animations + for (std::size_t i = 0; i < skeletonData->animationCount; ++i) + { + AnimationData* animationData = &skeletonData->animations[i]; + Animation* animation = new Animation(); + animation->setName(animationData->name); + animation->setTimeFrame(animationData->startTime, animationData->endTime); + + for (std::size_t j = 0; j < animationData->channelCount; ++j) + { + ChannelData* channelData = &animationData->channels[j]; + AnimationChannel* channel = animation->createChannel(channelData->id); + for (std::size_t k = 0; k < channelData->keyFrameCount; ++k) + { + KeyFrameData* keyFrameData = &channelData->keyFrames[k]; + KeyFrame* keyFrame = channel->insertKeyFrame(keyFrameData->time); + keyFrame->setTransform(keyFrameData->transform); + } + } + + // Add animation to skeleton + skeleton->addAnimation(animation); + } + // Add skeleton to model model->setSkeleton(skeleton); } @@ -359,9 +452,22 @@ Model* ModelLoader::load(const std::string& filename) { for (std::size_t i = 0; i < skeletonData->boneCount; ++i) { - delete[] skeletonData->boneData[i].children; + delete[] skeletonData->bones[i].children; + } + delete[] skeletonData->bones; + + for (std::size_t i = 0; i < skeletonData->animationCount; ++i) + { + AnimationData* animation = &skeletonData->animations[i]; + for (std::size_t j = 0; j < animation->channelCount; ++j) + { + delete[] animation->channels[j].keyFrames; + } + + delete[] animation->channels; } - delete[] skeletonData->boneData; + delete[] skeletonData->animations; + delete skeletonData; } diff --git a/src/model-loader.hpp b/src/model-loader.hpp index 4a1d966..5008d29 100644 --- a/src/model-loader.hpp +++ b/src/model-loader.hpp @@ -74,10 +74,34 @@ private: float length; }; + struct KeyFrameData + { + float time; + Transform transform; + }; + + struct ChannelData + { + std::uint16_t id; + std::uint16_t keyFrameCount; + KeyFrameData* keyFrames; + }; + + struct AnimationData + { + std::string name; + float startTime; + float endTime; + std::uint16_t channelCount; + ChannelData* channels; + }; + struct SkeletonData { std::uint16_t boneCount; - BoneData* boneData; + BoneData* bones; + std::uint16_t animationCount; + AnimationData* animations; }; static void constructBoneHierarchy(Bone* bone, const BoneData* data, std::uint16_t index);