fix up morph target animations and add stopAnimation function
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user