initial work to split into dart_filament and flutter_filament

This commit is contained in:
Nick Fisher
2024-04-30 12:07:26 +08:00
parent b81f34cd29
commit 8f9e309c34
1624 changed files with 165260 additions and 6619 deletions

View File

@@ -0,0 +1,280 @@
#pragma once
#include "Log.hpp"
#include <chrono>
#include <variant>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Texture.h>
#include <filament/TransformManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <gltfio/Animator.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <utils/NameComponentManager.h>
template class std::vector<float>;
namespace flutter_filament
{
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using namespace std::chrono;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> 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<float> frameData;
std::vector<int> 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<utils::Entity> meshTargets;
size_t skinIndex = 0;
int lengthInFrames;
float frameLengthInMs = 0;
std::vector<math::mat4f> frameData;
};
struct AnimationComponent
{
std::variant<FilamentInstance *, Entity> target;
std::vector<math::mat4f> initialJointTransforms;
std::vector<GltfAnimation> gltfAnimations;
std::vector<MorphAnimation> morphAnimations;
std::vector<BoneAnimation> 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<AnimationComponent>
{
filament::TransformManager &_transformManager;
filament::RenderableManager &_renderableManager;
public:
AnimationComponentManager(
filament::TransformManager &transformManager,
filament::RenderableManager &renderableManager) : _transformManager(transformManager),
_renderableManager(renderableManager){};
void addAnimationComponent(std::variant<FilamentInstance *, Entity> target)
{
AnimationComponent animationComponent;
animationComponent.target = target;
EntityInstanceBase::Type componentInstance;
if (std::holds_alternative<FilamentInstance *>(target))
{
auto instance = std::get<FilamentInstance *>(target);
const auto joints = instance->getJointsAt(0);
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);
}
componentInstance = addComponent(instance->getRoot());
}
else
{
componentInstance = addComponent(std::get<Entity>(target));
}
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 &morphAnimations = animationComponent.morphAnimations;
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 animationStatus = animationComponent.gltfAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(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)boneAnimations.size() - 1; i >= 0; i--)
{
auto animationStatus = boneAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(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<int>(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 = target->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;
}
}
}
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);
}
}
}
}
};
}

View File

@@ -0,0 +1,100 @@
#pragma once
#include "utils/Entity.h"
#include "utils/EntityInstance.h"
#include "utils/SingleInstanceComponentManager.h"
#include "filament/TransformManager.h"
#include "gltfio/FilamentAsset.h"
#include "gltfio/FilamentInstance.h"
#include "Log.hpp"
namespace flutter_filament
{
typedef void(*CollisionCallback)(int32_t entityId1, int32_t entityId2) ;
class CollisionComponentManager : public utils::SingleInstanceComponentManager<filament::Aabb, CollisionCallback, bool> {
const filament::TransformManager& _transformManager;
public:
CollisionComponentManager(const filament::TransformManager& transformManager) : _transformManager(transformManager) {}
std::vector<filament::math::float3> collides(utils::Entity transformingEntity, filament::Aabb sourceBox) {
auto sourceCorners = sourceBox.getCorners();
std::vector<filament::math::float3> 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);
auto targetCorners = targetBox.getCorners();
// Log("Checking collision for entity %d with aabb extent %f %f %f against source entity %d with aabb extent %f %f %f", entity, targetBox.extent().x, targetBox.extent().y, targetBox.extent().z, transformingEntityId, sourceBox.extent().x, sourceBox.extent().y, sourceBox.extent().z);
bool collided = false;
// iterate over every vertex in the source/target AABB
for(int i = 0; i < 8; i++) {
auto intersecting = sourceCorners.vertices[i];
auto min = targetBox.min;
auto max = targetBox.max;
// if the vertex has insersected with the target/source AABB
if(targetBox.contains(sourceCorners.vertices[i]) <= 0) {
collided = true;
// Log("targetBox %f %f %f contains source vertex %f %f %f", targetBox.extent().x, targetBox.extent().y, targetBox.extent().z, sourceCorners.vertices[i].x, sourceCorners.vertices[i].y, sourceCorners.vertices[i].z);
} else if(sourceBox.contains(targetCorners.vertices[i]) <= 0) {
// Log("sourceBox %f %f %f contains target vertex %f %f %f", sourceBox.extent().x, sourceBox.extent().y, sourceBox.extent().z, targetCorners.vertices[i].x, targetCorners.vertices[i].y, targetCorners.vertices[i].z);
collided = true;
intersecting = targetCorners.vertices[i];
min = sourceBox.min;
max = sourceBox.max;
} else {
continue;
}
auto affectsTransform = elementAt<2>(it);
if(affectsTransform) {
float xmin = min.x - intersecting.x;
float ymin = min.y - intersecting.y;
float zmin = min.z - intersecting.z;
float xmax = intersecting.x - max.x;
float ymax = intersecting.y - max.y;
float zmax = intersecting.z - max.z;
auto maxD = std::max(xmin,std::max(ymin,std::max(zmin,std::max(xmax,std::max(ymax,zmax)))));
filament::math::float3 axis;
if(maxD == xmin) {
axis = {-1.0f,0.0f, 0.0f};
} else if(maxD == ymin) {
axis = {0.0f,-1.0f, 0.0f};
} else if(maxD == zmin) {
axis = {0.0f,0.0f, -1.0f};
} else if(maxD == xmax) {
axis = {1.0f,0.0f, 0.0f};
} else if(maxD == ymax) {
axis = {0.0f,1.0f, 0.0f};
} else {
axis = { 0.0f, 0.0f, 1.0f};
}
collisionAxes.push_back(axis);
}
break;
}
if(collided) {
auto callback = elementAt<1>(it);
if(callback) {
callback(utils::Entity::smuggle(entity), utils::Entity::smuggle(transformingEntity));
}
}
}
return collisionAxes;
}
};
}