restructure embedded animations to allow simultaneous play
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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: () {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user