diff --git a/ios/include/components/AnimationComponentManager.hpp b/ios/include/components/AnimationComponentManager.hpp new file mode 100644 index 00000000..98f1eb29 --- /dev/null +++ b/ios/include/components/AnimationComponentManager.hpp @@ -0,0 +1,244 @@ +#pragma once + +#include "Log.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +template class std::vector; +namespace flutter_filament { + using namespace filament; + using namespace filament::gltfio; + using namespace utils; + using namespace std::chrono; + + typedef std::chrono::time_point time_point_t; + + enum AnimationType { + MORPH, BONE, GLTF + }; + + struct AnimationStatus { + time_point_t start = time_point_t::max(); + bool loop = false; + bool reverse = false; + float durationInSecs = 0; + }; + + struct GltfAnimation : AnimationStatus { + int index = -1; + }; + + // + // Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation. + // + struct MorphAnimation : AnimationStatus { + utils::Entity meshTarget; + int numFrames = -1; + float frameLengthInMs = 0; + std::vector frameData; + std::vector morphIndices; + int lengthInFrames; + }; + + // + // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. + // + struct BoneAnimation : AnimationStatus { + size_t boneIndex; + std::vector meshTargets; + size_t skinIndex = 0; + int lengthInFrames; + float frameLengthInMs = 0; + std::vector frameData; + }; + + struct AnimationComponent { + FilamentInstance* instance; + std::vector initialJointTransforms; + std::vector gltfAnimations; + std::vector morphAnimations; + std::vector boneAnimations; + + // the index of the last active glTF animation, + // used to cross-fade + int fadeGltfAnimationIndex = -1; + float fadeDuration = 0.0f; + float fadeOutAnimationStart = 0.0f; + + }; + + + class AnimationComponentManager : public utils::SingleInstanceComponentManager { + + filament::TransformManager& _transformManager; + filament::RenderableManager& _renderableManager; + + public: + AnimationComponentManager( + filament::TransformManager& transformManager, + filament::RenderableManager& renderableManager) : + _transformManager(transformManager), + _renderableManager(renderableManager) {}; + + void addAnimationComponent(FilamentInstance* instance) { + const auto joints = instance->getJointsAt(0); + AnimationComponent animationComponent; + animationComponent.instance = instance; + for(int i = 0; i < instance->getJointCountAt(0); i++) { + const auto joint = joints[i]; + const auto& jointTransformInstance = _transformManager.getInstance(joint); + const auto& jointTransform = _transformManager.getTransform(jointTransformInstance); + animationComponent.initialJointTransforms.push_back(jointTransform); + } + auto componentInstance = addComponent(instance->getRoot()); + this->elementAt<0>(componentInstance) = animationComponent; + } + + void update() + { + auto now = high_resolution_clock::now(); + + for(auto it = begin(); it < end(); it++) { + const auto& entity = getEntity(it); + + auto componentInstance = getInstance(entity); + auto& animationComponent = elementAt<0>(componentInstance); + + 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 elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001); + animator->updateBoneMatrices(); + gltfAnimations.erase(gltfAnimations.begin() + i); + animationComponent.fadeGltfAnimationIndex = -1; + continue; + } + animator->applyAnimation(animationStatus.index, elapsedInSecs); + + if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration) + { + // cross-fade + auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs; + auto alpha = elapsedInSecs / animationComponent.fadeDuration; + animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha); + } + } + + animator->updateBoneMatrices(); + + for (int i = (int)morphAnimations.size() - 1; i >= 0; i--) { + + auto animationStatus = morphAnimations[i]; + + auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + morphAnimations.erase(morphAnimations.begin() + i); + continue; + } + + + int frameNumber = static_cast(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 elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + boneAnimations.erase(boneAnimations.begin() + i); + continue; + } + + float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs; + + int currFrame = static_cast(elapsedFrames) % animationStatus.lengthInFrames; + float delta = elapsedFrames - currFrame; + int nextFrame = currFrame; + auto restLocalTransform = animationComponent.initialJointTransforms[animationStatus.boneIndex]; + + // offset from the end if reverse + if (animationStatus.reverse) + { + currFrame = animationStatus.lengthInFrames - currFrame; + if(currFrame > 0) { + nextFrame = currFrame - 1; + } else { + nextFrame = 0; + } + } else { + if(currFrame < animationStatus.lengthInFrames - 1) { + nextFrame = currFrame + 1; + } else { + nextFrame = currFrame; + } + } + + // simple linear interpolation + math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]); + math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]); + math::mat4f localTransform = curr + next; + + const Entity joint = animationComponent.instance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex]; + + auto jointTransform = _transformManager.getInstance(joint); + + _transformManager.setTransform(jointTransform, localTransform); + + animator->updateBoneMatrices(); + + if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + animationStatus.start = now; + } + } + } + } + }; + +} diff --git a/ios/include/components/StandardComponents.h b/ios/include/components/CollisionComponentManager.hpp similarity index 93% rename from ios/include/components/StandardComponents.h rename to ios/include/components/CollisionComponentManager.hpp index 31da4ab4..74d580a0 100644 --- a/ios/include/components/StandardComponents.h +++ b/ios/include/components/CollisionComponentManager.hpp @@ -1,5 +1,4 @@ -#ifndef _STANDARD_COMPONENTS_H -#define _STANDARD_COMPONENTS_H +#pragma once #include "utils/Entity.h" #include "utils/EntityInstance.h" @@ -9,7 +8,7 @@ #include "gltfio/FilamentInstance.h" #include "Log.hpp" -namespace polyvox +namespace flutter_filament { typedef void(*CollisionCallback)(int32_t entityId1, int32_t entityId2) ; @@ -19,11 +18,15 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager collides(EntityId transformingEntityId, filament::Aabb sourceBox) { + std::vector collides(utils::Entity transformingEntity, filament::Aabb sourceBox) { auto sourceCorners = sourceBox.getCorners(); std::vector collisionAxes; for(auto it = begin(); it < end(); it++) { auto entity = getEntity(it); + + if(entity == transformingEntity) { + continue; + } auto targetXformInstance = _transformManager.getInstance(entity); auto targetXform = _transformManager.getWorldTransform(targetXformInstance); auto targetBox = elementAt<0>(it).transform(targetXform); @@ -40,10 +43,10 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager(it); if(callback) { - callback(utils::Entity::smuggle(entity), transformingEntityId); + callback(utils::Entity::smuggle(entity), utils::Entity::smuggle(transformingEntity)); } } } @@ -92,7 +95,6 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager