restructure embedded animations to allow simultaneous play

This commit is contained in:
Nick Fisher
2022-08-13 12:52:53 +10:00
parent e51577cf6b
commit 7d24621925
5 changed files with 131 additions and 72 deletions

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9fb09944d65a155fc5b6522f296dd875df02fc2944733a35eb09bec23bbabdcd oid sha256:4a3a6b8b48db5d5e71ecd774629cfea00bcd75d4cf1462cb58448e1825c89815
size 813192 size 813688

View File

@@ -21,6 +21,7 @@ class _MyAppState extends State<MyApp> {
final weights = List.filled(255, 0.0); final weights = List.filled(255, 0.0);
List<String> _targets = []; List<String> _targets = [];
List<String> _animationNames = [];
bool _loop = false; bool _loop = false;
@override @override
@@ -69,11 +70,13 @@ class _MyAppState extends State<MyApp> {
}), }),
ElevatedButton( ElevatedButton(
child: const Text('load cube GLB'), child: const Text('load cube GLB'),
onPressed:_cube != null ? null : () async { onPressed:() async {
_cube = await _filamentController _cube = await _filamentController
.loadGlb('assets/cube.glb'); .loadGlb('assets/cube.glb');
print(await _filamentController
.getAnimationNames(_cube!)); _animationNames =
await _filamentController
.getAnimationNames(_cube!);
}), }),
ElevatedButton( ElevatedButton(
child: const Text('load cube GLTF'), child: const Text('load cube GLTF'),
@@ -111,8 +114,12 @@ class _MyAppState extends State<MyApp> {
.applyWeights(_cube!, List.filled(8, 0)); .applyWeights(_cube!, List.filled(8, 0));
}), }),
ElevatedButton( ElevatedButton(
onPressed: () => onPressed: () {
_filamentController.playAnimation(_cube!, 0, loop: _loop), _animationNames.asMap().forEach((idx, element) {
_filamentController.playAnimation(_cube!, idx, loop: _loop);
});
},
child: const Text('play animation')), child: const Text('play animation')),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {

View File

@@ -1,15 +1,15 @@
#include <chrono>
#include "SceneResources.hpp"
#include "SceneAsset.hpp" #include "SceneAsset.hpp"
#include "Log.hpp" #include "Log.hpp"
#include "SceneResources.hpp"
#include <chrono>
#include <gltfio/Animator.h> #include <gltfio/Animator.h>
#include <gltfio/AssetLoader.h> #include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h> #include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h> #include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h> #include <gltfio/TextureProvider.h>
#include <filament/TransformManager.h>
#include <filament/TransformManager.h>
using namespace std::chrono; using namespace std::chrono;
@@ -20,15 +20,21 @@ using namespace filament;
using namespace filament::gltfio; using namespace filament::gltfio;
using namespace utils; using namespace utils;
SceneAsset::SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm) SceneAsset::SceneAsset(FilamentAsset *asset, Engine *engine,
NameComponentManager *ncm)
: _asset(asset), _engine(engine), _ncm(ncm) { : _asset(asset), _engine(engine), _ncm(ncm) {
_animator = _asset->getAnimator(); _animator = _asset->getAnimator();
for (int i = 0; i < _animator->getAnimationCount(); i++) {
_embeddedAnimationStatus.push_back(
EmbeddedAnimationStatus(i, _animator->getAnimationDuration(i), false));
} }
Log("Created animation buffers for %d", _embeddedAnimationStatus.size());
}
SceneAsset::~SceneAsset() { _asset = nullptr; } SceneAsset::~SceneAsset() { _asset = nullptr; }
void SceneAsset::applyWeights(float *weights, int count) { void SceneAsset::applyWeights(float *weights, int count) {
RenderableManager& rm = _engine->getRenderableManager(); RenderableManager &rm = _engine->getRenderableManager();
for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) { for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) {
auto inst = rm.getInstance(_asset->getEntities()[i]); auto inst = rm.getInstance(_asset->getEntities()[i]);
rm.setMorphWeights(inst, weights, count); rm.setMorphWeights(inst, weights, count);
@@ -46,7 +52,7 @@ void SceneAsset::animateWeights(float *data, int numWeights, int numFrames,
void SceneAsset::updateAnimations() { void SceneAsset::updateAnimations() {
updateMorphAnimation(); updateMorphAnimation();
updateEmbeddedAnimation(); updateEmbeddedAnimations();
} }
void SceneAsset::updateMorphAnimation() { void SceneAsset::updateMorphAnimation() {
@@ -86,42 +92,60 @@ void SceneAsset::updateMorphAnimation() {
} }
void SceneAsset::playAnimation(int index, bool loop) { void SceneAsset::playAnimation(int index, bool loop) {
Log("Playing animation at index %d", index);
if (index > _animator->getAnimationCount() - 1) { if (index > _animator->getAnimationCount() - 1) {
Log("Asset does not contain an animation at index %d", index); Log("Asset does not contain an animation at index %d", index);
} else if (_embeddedAnimationStatus[index].started) {
Log("Animation already playing, call stop first.");
} else { } else {
_boneAnimationStatus = make_unique<BoneAnimationStatus>( Log("Starting animation at index %d", index);
index, _animator->getAnimationDuration(index), loop); _embeddedAnimationStatus[index].play = true;
_embeddedAnimationStatus[index].loop = loop;
} }
} }
void SceneAsset::stopAnimation() { void SceneAsset::stopAnimation(int index) {
// TODO - does this need to be threadsafe? // TODO - does this need to be threadsafe?
_boneAnimationStatus = nullptr; _embeddedAnimationStatus[index].play = false;
_embeddedAnimationStatus[index].started = false;
} }
void SceneAsset::updateEmbeddedAnimation() { void SceneAsset::updateEmbeddedAnimations() {
if (!_boneAnimationStatus) { auto now = high_resolution_clock::now();
return; for (auto &status : _embeddedAnimationStatus) {
} if (!status.play) {
// Log("Skipping animation %d", status.animationIndex);
duration<double> dur = duration_cast<duration<double>>( continue;
high_resolution_clock::now() - _boneAnimationStatus->lastTime); }
float startTime = 0; duration<double> dur =
if (!_boneAnimationStatus->hasStarted) { duration_cast<duration<double>>(now - status.startedAt);
_boneAnimationStatus->hasStarted = true; float animationTimeOffset = 0;
_boneAnimationStatus->lastTime = high_resolution_clock::now(); bool finished = false;
} else if (dur.count() >= _boneAnimationStatus->duration) { if (!status.started) {
if (_boneAnimationStatus->loop) { status.started = true;
_boneAnimationStatus->lastTime = high_resolution_clock::now(); status.startedAt = now;
} else { } else if (dur.count() >= status.duration) {
_boneAnimationStatus = nullptr; if (status.loop) {
return; status.startedAt = now;
} else {
finished = true;
}
} else {
animationTimeOffset = dur.count();
}
// Log("time offset %f", animationTimeOffset);
if (!finished) {
_animator->applyAnimation(status.animationIndex, animationTimeOffset);
} else {
Log("Animation %d finished", status.animationIndex);
status.play = false;
status.started = false;
} }
} else {
startTime = dur.count();
} }
_animator->applyAnimation(_boneAnimationStatus->animationIndex, startTime);
_animator->updateBoneMatrices(); _animator->updateBoneMatrices();
} }
@@ -165,29 +189,28 @@ unique_ptr<vector<string>> SceneAsset::getTargetNames(const char *meshName) {
return names; return names;
} }
void SceneAsset::transformToUnitCube() void SceneAsset::transformToUnitCube() {
{ if (!_asset) {
if (!_asset) Log("No asset, cannot transform.");
{ return;
Log("No asset, cannot transform.");
return;
}
auto &tm = _engine->getTransformManager();
auto aabb = _asset->getBoundingBox();
auto center = aabb.center();
auto halfExtent = aabb.extent();
auto maxExtent = max(halfExtent) * 2;
auto scaleFactor = 2.0f / maxExtent;
auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
tm.setTransform(tm.getInstance(_asset->getRoot()), transform);
} }
auto &tm = _engine->getTransformManager();
auto aabb = _asset->getBoundingBox();
auto center = aabb.center();
auto halfExtent = aabb.extent();
auto maxExtent = max(halfExtent) * 2;
auto scaleFactor = 2.0f / maxExtent;
auto transform =
math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
tm.setTransform(tm.getInstance(_asset->getRoot()), transform);
}
const utils::Entity* SceneAsset::getCameraEntities() { const utils::Entity *SceneAsset::getCameraEntities() {
return _asset->getCameraEntities(); return _asset->getCameraEntities();
} }
size_t SceneAsset::getCameraEntityCount() { size_t SceneAsset::getCameraEntityCount() {
return _asset->getCameraEntityCount(); return _asset->getCameraEntityCount();
} }
} // namespace polyvox } // namespace polyvox

View File

@@ -35,12 +35,12 @@ namespace polyvox {
void updateAnimations(); void updateAnimations();
/// ///
/// Immediately stop the currently playing animation. NOOP if no animation is playing. /// Immediately stop the animation at the specified index. Noop if no animation is playing.
/// ///
void stopAnimation(); void stopAnimation(int index);
/// ///
/// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely. /// Play the embedded animation (i.e. animation node embedded in the GLTF asset) under the specified index. If [loop] is true, the animation will repeat indefinitely.
/// ///
void playAnimation(int index, bool loop); void playAnimation(int index, bool loop);
@@ -69,15 +69,15 @@ namespace polyvox {
FilamentAsset* _asset = nullptr; FilamentAsset* _asset = nullptr;
Engine* _engine = nullptr; Engine* _engine = nullptr;
NameComponentManager* _ncm; NameComponentManager* _ncm;
void updateMorphAnimation(); void updateMorphAnimation();
void updateEmbeddedAnimation(); void updateEmbeddedAnimations();
Animator* _animator; Animator* _animator;
// animation flags; // animation flags;
bool isAnimating;
unique_ptr<MorphAnimationStatus> _morphAnimationBuffer; unique_ptr<MorphAnimationStatus> _morphAnimationBuffer;
unique_ptr<BoneAnimationStatus> _boneAnimationStatus; vector<EmbeddedAnimationStatus> _embeddedAnimationStatus;
}; };
} }

View File

@@ -52,14 +52,43 @@ namespace polyvox {
// //
// Holds the current state of a bone animation embeded in a GLTF asset. // Holds the current state of a bone animation embeded in a GLTF asset.
// Currently, an instance will be constructed for every animation in an asset whenever a SceneAsset is created (and thus will persist for the lifetime of the SceneAsset).
// //
struct BoneAnimationStatus { struct EmbeddedAnimationStatus {
BoneAnimationStatus(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {} EmbeddedAnimationStatus(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {}
bool hasStarted = false;
int animationIndex; //
float duration = 0; // A flag that is checked each frame to determine whether or not the animation should play.
time_point_t lastTime; //
bool play;
//
// If [play] is true, this flag will be checked when the animation is complete. If true, the animation will restart.
//
bool loop; bool loop;
//
// If [play] is true, this flag will be set to true when the animation is started.
//
bool started = false;
//
// The index of the animation in the GLTF asset.
//
int animationIndex;
//
// The duration of the animation (calculated from the GLTF animator).
//
float duration = 0;
//
// The time point at which this animation was last started.
// This is used to calculate the "animation time offset" that is passed to the Animator.
//
time_point_t startedAt;
}; };
// //