use std::variant for AnimationComponent to distinguish between arbitrray entities and glTF FilamentAsset

This commit is contained in:
Nick Fisher
2024-04-26 10:56:30 +08:00
parent b5a36cc8d8
commit ff25744a84

View File

@@ -3,6 +3,7 @@
#include "Log.hpp" #include "Log.hpp"
#include <chrono> #include <chrono>
#include <variant>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/RenderableManager.h> #include <filament/RenderableManager.h>
@@ -22,33 +23,40 @@
#include <utils/NameComponentManager.h> #include <utils/NameComponentManager.h>
template class std::vector<float>; template class std::vector<float>;
namespace flutter_filament { namespace flutter_filament
{
using namespace filament; using namespace filament;
using namespace filament::gltfio; using namespace filament::gltfio;
using namespace utils; using namespace utils;
using namespace std::chrono; using namespace std::chrono;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t; typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
enum AnimationType { enum AnimationType
MORPH, BONE, GLTF {
MORPH,
BONE,
GLTF
}; };
struct AnimationStatus { struct AnimationStatus
{
time_point_t start = time_point_t::max(); time_point_t start = time_point_t::max();
bool loop = false; bool loop = false;
bool reverse = false; bool reverse = false;
float durationInSecs = 0; float durationInSecs = 0;
}; };
struct GltfAnimation : AnimationStatus { struct GltfAnimation : AnimationStatus
int index = -1; {
int index = -1;
}; };
// //
// Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation. // Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation.
// //
struct MorphAnimation : AnimationStatus { struct MorphAnimation : AnimationStatus
{
utils::Entity meshTarget; utils::Entity meshTarget;
int numFrames = -1; int numFrames = -1;
float frameLengthInMs = 0; float frameLengthInMs = 0;
@@ -57,10 +65,11 @@ namespace flutter_filament {
int lengthInFrames; int lengthInFrames;
}; };
// //
// Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation.
// //
struct BoneAnimation : AnimationStatus { struct BoneAnimation : AnimationStatus
{
size_t boneIndex; size_t boneIndex;
std::vector<utils::Entity> meshTargets; std::vector<utils::Entity> meshTargets;
size_t skinIndex = 0; size_t skinIndex = 0;
@@ -69,67 +78,85 @@ namespace flutter_filament {
std::vector<math::mat4f> frameData; std::vector<math::mat4f> frameData;
}; };
struct AnimationComponent { struct AnimationComponent
FilamentInstance* instance; {
std::variant<FilamentInstance *, Entity> target;
std::vector<math::mat4f> initialJointTransforms; std::vector<math::mat4f> initialJointTransforms;
std::vector<GltfAnimation> gltfAnimations; std::vector<GltfAnimation> gltfAnimations;
std::vector<MorphAnimation> morphAnimations; std::vector<MorphAnimation> morphAnimations;
std::vector<BoneAnimation> boneAnimations; std::vector<BoneAnimation> boneAnimations;
// the index of the last active glTF animation, // the index of the last active glTF animation,
// used to cross-fade // used to cross-fade
int fadeGltfAnimationIndex = -1; int fadeGltfAnimationIndex = -1;
float fadeDuration = 0.0f; float fadeDuration = 0.0f;
float fadeOutAnimationStart = 0.0f; float fadeOutAnimationStart = 0.0f;
}; };
class AnimationComponentManager : public utils::SingleInstanceComponentManager<AnimationComponent>
{
class AnimationComponentManager : public utils::SingleInstanceComponentManager<AnimationComponent> { filament::TransformManager &_transformManager;
filament::RenderableManager &_renderableManager;
filament::TransformManager& _transformManager; public:
filament::RenderableManager& _renderableManager; AnimationComponentManager(
filament::TransformManager &transformManager,
filament::RenderableManager &renderableManager) : _transformManager(transformManager),
_renderableManager(renderableManager){};
public: void addAnimationComponent(std::variant<FilamentInstance *, Entity> target)
AnimationComponentManager( {
filament::TransformManager& transformManager, AnimationComponent animationComponent;
filament::RenderableManager& renderableManager) : animationComponent.target = target;
_transformManager(transformManager), EntityInstanceBase::Type componentInstance;
_renderableManager(renderableManager) {}; if (std::holds_alternative<FilamentInstance *>(target))
{
void addAnimationComponent(FilamentInstance* instance) { auto instance = std::get<FilamentInstance *>(target);
const auto joints = instance->getJointsAt(0); const auto joints = instance->getJointsAt(0);
AnimationComponent animationComponent;
animationComponent.instance = instance; for (int i = 0; i < instance->getJointCountAt(0); i++)
for(int i = 0; i < instance->getJointCountAt(0); i++) { {
const auto joint = joints[i]; const auto joint = joints[i];
const auto& jointTransformInstance = _transformManager.getInstance(joint); const auto &jointTransformInstance = _transformManager.getInstance(joint);
const auto& jointTransform = _transformManager.getTransform(jointTransformInstance); const auto &jointTransform = _transformManager.getTransform(jointTransformInstance);
animationComponent.initialJointTransforms.push_back(jointTransform); animationComponent.initialJointTransforms.push_back(jointTransform);
} }
auto componentInstance = addComponent(instance->getRoot()); componentInstance = addComponent(instance->getRoot());
this->elementAt<0>(componentInstance) = animationComponent; }
else
{
componentInstance = addComponent(std::get<Entity>(target));
} }
void update() this->elementAt<0>(componentInstance) = animationComponent;
}
void update()
{
auto now = high_resolution_clock::now();
for (auto it = begin(); it < end(); it++)
{ {
auto now = high_resolution_clock::now(); const auto &entity = getEntity(it);
// Log("animation component count : %d", ) Log("Updating anim for entity %d", entity);
for(auto it = begin(); it < end(); it++) { auto componentInstance = getInstance(entity);
const auto& entity = getEntity(it); auto &animationComponent = elementAt<0>(componentInstance);
auto componentInstance = getInstance(entity); auto &morphAnimations = animationComponent.morphAnimations;
auto& animationComponent = elementAt<0>(componentInstance);
if (std::holds_alternative<FilamentInstance *>(animationComponent.target))
{
auto target = std::get<FilamentInstance *>(animationComponent.target);
auto animator = target->getAnimator();
auto &gltfAnimations = animationComponent.gltfAnimations;
auto &boneAnimations = animationComponent.boneAnimations;
for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--)
{
auto animator = animationComponent.instance->getAnimator();
auto& gltfAnimations = animationComponent.gltfAnimations;
auto& morphAnimations = animationComponent.morphAnimations;
auto& boneAnimations = animationComponent.boneAnimations;
for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--) {
auto animationStatus = animationComponent.gltfAnimations[i]; auto animationStatus = animationComponent.gltfAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f; auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
@@ -155,38 +182,8 @@ namespace flutter_filament {
animator->updateBoneMatrices(); animator->updateBoneMatrices();
for (int i = (int)morphAnimations.size() - 1; i >= 0; i--) { for (int i = (int)boneAnimations.size() - 1; i >= 0; i--)
{
auto animationStatus = morphAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
morphAnimations.erase(morphAnimations.begin() + i);
continue;
}
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames;
// offset from the end if reverse
if (animationStatus.reverse)
{
frameNumber = animationStatus.lengthInFrames - frameNumber;
}
auto baseOffset = frameNumber * animationStatus.morphIndices.size();
for (int i = 0; i < animationStatus.morphIndices.size(); i++)
{
auto morphIndex = animationStatus.morphIndices[i];
// set the weights appropriately
_renderableManager.setMorphWeights(
_renderableManager.getInstance(animationStatus.meshTarget),
animationStatus.frameData.data() + baseOffset + i,
1,
morphIndex);
}
}
for (int i = (int)boneAnimations.size() - 1; i >= 0; i--) {
auto animationStatus = boneAnimations[i]; auto animationStatus = boneAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f; auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
@@ -196,9 +193,9 @@ namespace flutter_filament {
boneAnimations.erase(boneAnimations.begin() + i); boneAnimations.erase(boneAnimations.begin() + i);
continue; continue;
} }
float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs; float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs;
int currFrame = static_cast<int>(elapsedFrames) % animationStatus.lengthInFrames; int currFrame = static_cast<int>(elapsedFrames) % animationStatus.lengthInFrames;
float delta = elapsedFrames - currFrame; float delta = elapsedFrames - currFrame;
int nextFrame = currFrame; int nextFrame = currFrame;
@@ -208,30 +205,38 @@ namespace flutter_filament {
if (animationStatus.reverse) if (animationStatus.reverse)
{ {
currFrame = animationStatus.lengthInFrames - currFrame; currFrame = animationStatus.lengthInFrames - currFrame;
if(currFrame > 0) { if (currFrame > 0)
{
nextFrame = currFrame - 1; nextFrame = currFrame - 1;
} else { }
else
{
nextFrame = 0; nextFrame = 0;
} }
} else { }
if(currFrame < animationStatus.lengthInFrames - 1) { else
{
if (currFrame < animationStatus.lengthInFrames - 1)
{
nextFrame = currFrame + 1; nextFrame = currFrame + 1;
} else { }
else
{
nextFrame = currFrame; nextFrame = currFrame;
} }
} }
// simple linear interpolation // simple linear interpolation
math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]); math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]);
math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]); math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]);
math::mat4f localTransform = curr + next; math::mat4f localTransform = curr + next;
const Entity joint = animationComponent.instance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex]; const Entity joint = target->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
auto jointTransform = _transformManager.getInstance(joint); auto jointTransform = _transformManager.getInstance(joint);
_transformManager.setTransform(jointTransform, localTransform); _transformManager.setTransform(jointTransform, localTransform);
animator->updateBoneMatrices(); animator->updateBoneMatrices();
if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
@@ -240,7 +245,38 @@ namespace flutter_filament {
} }
} }
} }
} for (int i = (int)morphAnimations.size() - 1; i >= 0; i--)
}; {
auto animationStatus = morphAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
morphAnimations.erase(morphAnimations.begin() + i);
continue;
}
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames;
// offset from the end if reverse
if (animationStatus.reverse)
{
frameNumber = animationStatus.lengthInFrames - frameNumber;
}
auto baseOffset = frameNumber * animationStatus.morphIndices.size();
for (int i = 0; i < animationStatus.morphIndices.size(); i++)
{
auto morphIndex = animationStatus.morphIndices[i];
// set the weights appropriately
_renderableManager.setMorphWeights(
_renderableManager.getInstance(animationStatus.meshTarget),
animationStatus.frameData.data() + baseOffset + i,
1,
morphIndex);
}
}
}
}
};
} }