use std::variant for AnimationComponent to distinguish between arbitrray entities and glTF FilamentAsset
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user