fix up morph target animations and add stopAnimation function

This commit is contained in:
Nick Fisher
2022-04-28 15:54:09 +08:00
parent 2de1fedaf3
commit 94d1329f0f
4 changed files with 73 additions and 31 deletions

View File

@@ -81,6 +81,11 @@ class _MyAppState extends State<MyApp> {
onPressed: () => onPressed: () =>
_filamentController.playAnimation(0, loop: _loop), _filamentController.playAnimation(0, loop: _loop),
child: const Text('play animation')), child: const Text('play animation')),
ElevatedButton(
onPressed: () {
_filamentController.stopAnimation();
},
child: const Text('stop animation')),
Checkbox( Checkbox(
onChanged: (_) => setState(() { onChanged: (_) => setState(() {
_loop = !_loop; _loop = !_loop;
@@ -108,13 +113,15 @@ class _MyAppState extends State<MyApp> {
final numWeights = 8; final numWeights = 8;
final totalFrames = framerate * totalSecs; final totalFrames = framerate * totalSecs;
final frames = List.generate( final frames = List.generate(
totalFrames, totalFrames,
(frame) => List.filled( (frame) =>
numWeights, frame / totalFrames)) List.filled(numWeights, frame / totalFrames));
.reduce((accum, next) => accum + next);
_filamentController.animate( _filamentController.animate(
frames, numWeights, framerate.toDouble()); frames.reduce((a, b) => a + b),
numWeights,
totalFrames,
1000 / framerate.toDouble());
}, },
child: const Text('animate weights')), child: const Text('animate weights')),
Builder( Builder(

View File

@@ -539,9 +539,11 @@ namespace polyvox
Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, contentScaleFactor); Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, contentScaleFactor);
} }
void FilamentViewer::animateWeights(float *data, int numWeights, int numFrames, float frameRate) void FilamentViewer::animateWeights(float *data, int numWeights, int numFrames, float frameLengthInMs)
{ {
morphAnimationBuffer = std::make_unique<MorphAnimationBuffer>(data, numWeights, numFrames, 1000 / frameRate); // assert numWeights == asset.numWeights ?
Log("Making morph animation buffer with %d weights across %d frames and frame length %f ms ", numWeights, numFrames, frameLengthInMs);
morphAnimationBuffer = std::make_unique<MorphAnimationBuffer>(data, numWeights, numFrames, frameLengthInMs);
} }
void FilamentViewer::updateMorphAnimation() void FilamentViewer::updateMorphAnimation()
@@ -549,20 +551,19 @@ namespace polyvox
if (morphAnimationBuffer->frameIndex >= morphAnimationBuffer->numFrames) if (morphAnimationBuffer->frameIndex >= morphAnimationBuffer->numFrames)
{ {
duration<double, std::milli> dur = high_resolution_clock::now() - morphAnimationBuffer->startTime;
Log("Morph animation completed in %f ms (%d frames at framerate %f), final frame was %d", dur.count(), morphAnimationBuffer->numFrames, 1000 / morphAnimationBuffer->frameLengthInMs, morphAnimationBuffer->frameIndex);
morphAnimationBuffer = nullptr; morphAnimationBuffer = nullptr;
return; } else if (morphAnimationBuffer->frameIndex == -1)
}
if (morphAnimationBuffer->frameIndex == -1)
{ {
morphAnimationBuffer->frameIndex++; morphAnimationBuffer->frameIndex++;
morphAnimationBuffer->startTime = std::chrono::high_resolution_clock::now(); morphAnimationBuffer->startTime = high_resolution_clock::now();
applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights);
} }
else else
{ {
std::chrono::duration<double, std::milli> dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->startTime; duration<double, std::milli> dur = high_resolution_clock::now() - morphAnimationBuffer->startTime;
int frameIndex = dur.count() / morphAnimationBuffer->frameLength; int frameIndex = static_cast<int>(dur.count() / morphAnimationBuffer->frameLengthInMs);
if (frameIndex != morphAnimationBuffer->frameIndex) if (frameIndex != morphAnimationBuffer->frameIndex)
{ {
morphAnimationBuffer->frameIndex = frameIndex; morphAnimationBuffer->frameIndex = frameIndex;
@@ -579,15 +580,20 @@ namespace polyvox
} }
} }
void FilamentViewer::stopAnimation() {
// TODO - does this need to be threadsafe?
embeddedAnimationBuffer = nullptr;
}
void FilamentViewer::updateEmbeddedAnimation() { void FilamentViewer::updateEmbeddedAnimation() {
duration<double> dur = duration_cast<duration<double>>(std::chrono::high_resolution_clock::now() - embeddedAnimationBuffer->lastTime); duration<double> dur = duration_cast<duration<double>>(high_resolution_clock::now() - embeddedAnimationBuffer->lastTime);
float startTime = 0; float startTime = 0;
if(!embeddedAnimationBuffer->hasStarted) { if(!embeddedAnimationBuffer->hasStarted) {
embeddedAnimationBuffer->hasStarted = true; embeddedAnimationBuffer->hasStarted = true;
embeddedAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now(); embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
} else if(dur.count() >= embeddedAnimationBuffer->duration) { } else if(dur.count() >= embeddedAnimationBuffer->duration) {
if(embeddedAnimationBuffer->loop) { if(embeddedAnimationBuffer->loop) {
embeddedAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now(); embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
} else { } else {
embeddedAnimationBuffer = nullptr; embeddedAnimationBuffer = nullptr;
return; return;

View File

@@ -76,12 +76,12 @@ namespace polyvox {
MorphAnimationBuffer(float* frameData, MorphAnimationBuffer(float* frameData,
int numWeights, int numWeights,
int numFrames, int numFrames,
float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLength(frameLength) { float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLengthInMs(frameLength) {
} }
int frameIndex = -1; int frameIndex = -1;
int numFrames; int numFrames;
float frameLength; float frameLengthInMs;
time_point_t startTime; time_point_t startTime;
float* frameData; float* frameData;
@@ -102,10 +102,32 @@ namespace polyvox {
unique_ptr<vector<string>> getTargetNames(const char* meshName); unique_ptr<vector<string>> getTargetNames(const char* meshName);
unique_ptr<vector<string>> getAnimationNames(); unique_ptr<vector<string>> getAnimationNames();
Manipulator<float>* manipulator; Manipulator<float>* manipulator;
///
/// Manually set the weights for all morph targets in the assets to the provided values.
/// See [animateWeights] if you want to automatically
///
void applyWeights(float* weights, int count); void applyWeights(float* weights, int count);
void animateWeights(float* data, int numWeights, int length, float frameRate);
///
/// Update the asset's morph target weights every "frame" (which is an arbitrary length of time, i.e. this is not the same as a frame at the framerate of the underlying rendering framework).
/// Accordingly:
/// length(data) = numWeights * numFrames
/// total_animation_duration_in_ms = number_of_frames * frameLengthInMs
///
void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs);
///
/// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely.
///
void playAnimation(int index, bool loop); void playAnimation(int index, bool loop);
///
/// Immediately stop the currently playing animation. NOOP if no animation is playing.
///
void stopAnimation();
bool setCamera(const char* nodeName); bool setCamera(const char* nodeName);
void destroySwapChain(); void destroySwapChain();
void createSwapChain(void* surface); void createSwapChain(void* surface);

View File

@@ -17,16 +17,18 @@ abstract class FilamentController {
Future<List<String>> getTargetNames(String meshName); Future<List<String>> getTargetNames(String meshName);
Future<List<String>> getAnimationNames(); Future<List<String>> getAnimationNames();
Future releaseSourceAssets(); Future releaseSourceAssets();
Future playAnimation(int index, {bool loop=false}); Future playAnimation(int index, {bool loop = false});
Future stopAnimation();
Future setCamera(String name); Future setCamera(String name);
/// ///
/// Set the weights of all morph targets in the mesh to the specified weights at successive frames (where [framerate] is the number of times per second the weights should be updated). /// Set the weights of all morph targets in the mesh to the specified weights at successive frames (where each frame requires a duration of [frameLengthInMs].
/// Accepts a list of doubles representing a sequence of "frames", stacked end-to-end. /// Accepts a list of doubles representing a sequence of "frames", stacked end-to-end.
/// Each frame is [numWeights] in length, where each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame. /// Each frame is [numWeights] in length, where each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
/// In other words, weights is a contiguous sequence of floats of size W*F, where W is the number of weights and F is the number of frames /// In other words, weights is a contiguous sequence of floats of size W*F, where W is the number of weights and F is the number of frames
/// ///
Future animate(List<double> weights, int numWeights, double frameRate); Future animate(
List<double> data, int numWeights, int numFrames, double frameLengthInMs);
Future createMorpher(String meshName, List<int> primitives); Future createMorpher(String meshName, List<int> primitives);
Future zoom(double z); Future zoom(double z);
} }
@@ -45,7 +47,7 @@ class PolyvoxFilamentController extends FilamentController {
_channel = MethodChannel("app.polyvox.filament/filament_view_$id"); _channel = MethodChannel("app.polyvox.filament/filament_view_$id");
_channel.setMethodCallHandler((call) async { _channel.setMethodCallHandler((call) async {
print("Received Filament method channel call : ${call.method}"); print("Received Filament method channel call : ${call.method}");
if(call.method == "ready") { if (call.method == "ready") {
onFilamentViewCreatedHandler?.call(_id); onFilamentViewCreatedHandler?.call(_id);
return Future.value(true); return Future.value(true);
} else { } else {
@@ -105,14 +107,15 @@ class PolyvoxFilamentController extends FilamentController {
} }
Future<List<String>> getAnimationNames() async { Future<List<String>> getAnimationNames() async {
var result = (await _channel.invokeMethod("getAnimationNames")) var result =
.cast<String>(); (await _channel.invokeMethod("getAnimationNames")).cast<String>();
return result; return result;
} }
Future animate(List<double> weights, int numWeights, double frameRate) async { Future animate(List<double> weights, int numWeights, int numFrames,
await _channel double frameLengthInMs) async {
.invokeMethod("animateWeights", [weights, numWeights, frameRate]); await _channel.invokeMethod(
"animateWeights", [weights, numWeights, numFrames, frameLengthInMs]);
} }
Future releaseSourceAssets() async { Future releaseSourceAssets() async {
@@ -127,8 +130,12 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("createMorpher", [meshName, primitives]); await _channel.invokeMethod("createMorpher", [meshName, primitives]);
} }
Future playAnimation(int index, {bool loop=false}) async { Future playAnimation(int index, {bool loop = false}) async {
await _channel.invokeMethod("playAnimation", [index,loop]); await _channel.invokeMethod("playAnimation", [index, loop]);
}
Future stopAnimation() async {
await _channel.invokeMethod("stopAnimation");
} }
Future setCamera(String name) async { Future setCamera(String name) async {