From 9892f363634bed458ce5ae3e80d1e95391bc277d Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 7 Feb 2022 14:52:03 +0800 Subject: [PATCH] re-add morph animation --- android/src/main/cpp/filament_api.cpp | 11 +- .../app/polyvox/filament/FilamentInterop.kt | 6 +- .../app/polyvox/filament/FilamentView.kt | 29 ++++- example/lib/main.dart | 70 ++++++----- ios/Classes/FilamentMethodCallHandler.mm | 2 +- ios/src/FilamentViewer.cpp | 112 ++++++++---------- ios/src/FilamentViewer.hpp | 50 ++++---- 7 files changed, 160 insertions(+), 120 deletions(-) diff --git a/android/src/main/cpp/filament_api.cpp b/android/src/main/cpp/filament_api.cpp index 89f1b322..587c7eb6 100644 --- a/android/src/main/cpp/filament_api.cpp +++ b/android/src/main/cpp/filament_api.cpp @@ -117,6 +117,11 @@ extern "C" { ((FilamentViewer*)viewer)->applyWeights(weights, count); } + void animate_weights(void* viewer, float* data, int numWeights, int numFrames, float frameRate) { + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Animating %d frames, each with %d weights", numFrames, numWeights); + ((FilamentViewer*)viewer)->animateWeights((float*)data, numWeights, numFrames, frameRate); + } + void get_target_names(void* viewer, char* meshName, char*** outPtr, int* countPtr ) { StringList names = ((FilamentViewer*)viewer)->getTargetNames(meshName); @@ -136,9 +141,11 @@ extern "C" { void free_pointer(char*** ptr, int size) { __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Freeing %d char pointers", size); for(int i = 0; i < size; i++) { - free((*ptr)[i]); + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "%d", i); + // free((*ptr)[i]); } - free(*ptr); + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Free complete"); + // free(*ptr); } void release_source_assets(void* viewer) { diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt index 4ddfcfc5..1614932c 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt @@ -32,7 +32,7 @@ interface FilamentInterop : Library { fun load_gltf(viewer:Pointer, uri:String, relativeResourcePath:String) : Pointer; - fun set_camera(viewer:Pointer, nodeName:String) : Pointer; + fun set_camera(viewer:Pointer, nodeName:String) : Boolean; fun render(viewer:Pointer); @@ -52,9 +52,11 @@ interface FilamentInterop : Library { fun apply_weights(viewer:Pointer, weights:FloatArray, size:Int); + fun animate_weights(viewer:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float); + fun get_target_names(viewer:Pointer, meshName:String, outPtr:PointerByReference, outLen:IntByReference); - fun free_pointer(ptr:Pointer, size:Int) + fun free_pointer(ptr:PointerByReference, size:Int) fun release_source_assets(viewer:Pointer) diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt index 6325c020..60874590 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt @@ -182,11 +182,15 @@ PlatformView { "setCamera" -> { if (_viewer == null) return; - _lib.set_camera( + val success = _lib.set_camera( _viewer!!, call.arguments as String ) - result.success("OK"); + if(success) { + result.success("OK"); + } else { + result.error("failed","failed", "Failed to set camera") + } } "zoom" -> { if(_viewer == null) @@ -203,8 +207,14 @@ PlatformView { val names = arrPtr.value.getStringArray(0, countPtr.value); - _lib.free_pointer(arrPtr.value, countPtr.getValue()) - val namesAsList = names.toCollection(ArrayList()) + Log.v(TAG, "Got target names $names") + + val namesAsList = names.toCollection(ArrayList()) + + _lib.free_pointer(arrPtr, countPtr.getValue()) + + Log.v(TAG, "Free complete") + result.success(namesAsList) } "applyWeights" -> { @@ -215,6 +225,17 @@ PlatformView { _lib.apply_weights(_viewer!!, weights.toFloatArray(), weights.size) result.success("OK"); } + "animateWeights" -> { + if(_viewer == null) + return; + val args = call.arguments as ArrayList + val frames = args[0] as ArrayList; + val numWeights = args[1] as Int + val frameRate = args[2] as Double + + _lib.animate_weights(_viewer!!, frames.toFloatArray(), numWeights, (frames.size / numWeights).toInt(), frameRate.toFloat()) + result.success("OK"); + } "panStart" -> { val args = call.arguments as ArrayList _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true) diff --git a/example/lib/main.dart b/example/lib/main.dart index df82cc4e..2233be80 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -83,7 +83,6 @@ class _MyAppState extends State { _filamentController.zoom(-1.0); }, child: const Text('zoom in')), - ElevatedButton( onPressed: () { _filamentController.zoom(1.0); @@ -93,33 +92,50 @@ class _MyAppState extends State { onPressed: () { _filamentController.setCamera("Camera.001"); }, - child: const Text('Set Camera')), - Builder(builder:(innerCtx) => ElevatedButton( - onPressed: () async { - final names = await _filamentController - .getTargetNames("Cube.001"); - - await showDialog( - builder: (ctx) { - return Container( - color: Colors.white, - height:200, width:200, - child: Column( - mainAxisSize: MainAxisSize.min, - children: names - .map((name) => Text(name)) - .cast() - .toList() + - [ - ElevatedButton( - onPressed: () => - Navigator.of(ctx).pop(), - child: Text("Close")) - ])); - }, - context: innerCtx); + child: const Text('set camera')), + ElevatedButton( + onPressed: () { + final framerate = 30; + final totalSecs = 5; + final numWeights = 8; + final totalFrames = framerate * totalSecs; + final frames = List.generate( + totalFrames, + (frame) => List.filled( + numWeights, frame / totalFrames)) + .reduce((accum, next) => accum + next); + + _filamentController.animate(frames, numWeights, framerate.toDouble()); }, - child: const Text('get target names'))), + child: const Text('animate weights')), + Builder( + builder: (innerCtx) => ElevatedButton( + onPressed: () async { + final names = await _filamentController + .getTargetNames("Cube.001"); + + await showDialog( + builder: (ctx) { + return Container( + color: Colors.white, + height: 200, + width: 200, + child: Column( + mainAxisSize: MainAxisSize.min, + children: names + .map((name) => Text(name)) + .cast() + .toList() + + [ + ElevatedButton( + onPressed: () => + Navigator.of(ctx).pop(), + child: Text("Close")) + ])); + }, + context: innerCtx); + }, + child: const Text('get target names'))), ElevatedButton( onPressed: () async { await _filamentController.panStart(1, 1); diff --git a/ios/Classes/FilamentMethodCallHandler.mm b/ios/Classes/FilamentMethodCallHandler.mm index 148061f0..f5e95c21 100644 --- a/ios/Classes/FilamentMethodCallHandler.mm +++ b/ios/Classes/FilamentMethodCallHandler.mm @@ -109,7 +109,7 @@ static void* freeResourceGlobal(ResourceBuffer rb) { for(int i =0 ; i < [frameData count]; i++) { *(framesArr+i) = [[frameData objectAtIndex:i] floatValue]; } - _viewer->animateWeights((float*)framesArr, [numWeights intValue], [frameData count], [frameRate floatValue]); + _viewer->animateWeights((float*)framesArr, [numWeights intValue], [frameData count] / numWeights, [frameRate floatValue]); result(@"OK"); } else if([@"createMorpher" isEqualToString:call.method]) { const char* meshName = [call.arguments[0] UTF8String]; diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 32f665dc..47f0c569 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -517,6 +517,10 @@ void FilamentViewer::render() { return; } + if(morphAnimationBuffer) { + updateMorphAnimation(); + } + math::float3 eye, target, upward; manipulator->getLookAt(&eye, &target, &upward); _mainCamera->lookAt(eye, target, upward); @@ -544,52 +548,37 @@ void FilamentViewer::updateViewportAndCameraProjection(int width, int height, fl Log("Set viewport to %d %d", _width, _height); } +void FilamentViewer::animateWeights(float* data, int numWeights, int numFrames, float frameRate) { + morphAnimationBuffer = std::make_unique(data, numWeights, numFrames, 1000 / frameRate ); +} +void FilamentViewer::updateMorphAnimation() { + + if(morphAnimationBuffer->frameIndex >= morphAnimationBuffer->numFrames) { + morphAnimationBuffer = nullptr; + return; + } + + if(morphAnimationBuffer->frameIndex == -1) { + morphAnimationBuffer->frameIndex++; + morphAnimationBuffer->startTime = std::chrono::high_resolution_clock::now(); + applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); + } else { + std::chrono::duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->startTime; + int frameIndex = dur.count() / morphAnimationBuffer->frameLength; + if(frameIndex != morphAnimationBuffer->frameIndex) { + morphAnimationBuffer->frameIndex = frameIndex; + applyWeights(morphAnimationBuffer->frameData + (morphAnimationBuffer->frameIndex * morphAnimationBuffer->numWeights), morphAnimationBuffer->numWeights); + } + } + } - //void FilamentViewer::updateAnimation(AnimationBuffer animation, std::function moo) { -// if(morphAnimationBuffer.frameIndex >= animation.numFrames) { -// this.animation = null; -// return; -// } -// -// if(animation.frameIndex == -1) { -// animation->frameIndex++; -// animation->lastTime = std::chrono::high_resolution_clock::now(); -// callback(); // applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); -// } else { -// duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime; -// float msElapsed = dur.count(); -// if(msElapsed > animation->frameLength) { -// animation->frameIndex++; -// animation->lastTime = std::chrono::high_resolution_clock::now(); -// callback(); // applyWeights(frameData + (frameIndex * numWeights), numWeights); -// } -// } -//} -// void FilamentViewer::updateMorphAnimation() { - -// if(morphAnimationBuffer->frameIndex >= morphAnimationBuffer->numFrames) { -// morphAnimationBuffer = nullptr; -// return; -// } - -// if(morphAnimationBuffer->frameIndex == -1) { -// morphAnimationBuffer->frameIndex++; -// morphAnimationBuffer->startTime = std::chrono::high_resolution_clock::now(); -// applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); -// } else { -// std::chrono::duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->startTime; -// int frameIndex = dur.count() / morphAnimationBuffer->frameLength; -// if(frameIndex != morphAnimationBuffer->frameIndex) { -// morphAnimationBuffer->frameIndex = frameIndex; -// applyWeights(morphAnimationBuffer->frameData + (morphAnimationBuffer->frameIndex * morphAnimationBuffer->numWeights), morphAnimationBuffer->numWeights); -// } -// } - -// } + +} + // void FilamentViewer::updateEmbeddedAnimation() { // duration dur = duration_cast>(std::chrono::high_resolution_clock::now() - embeddedAnimationBuffer->lastTime); @@ -632,26 +621,6 @@ void FilamentViewer::updateViewportAndCameraProjection(int width, int height, fl // embeddedAnimationBuffer = make_unique(index, _animator->getAnimationDuration(index)); // } -// void FilamentViewer::animateWeights(float* data, int numWeights, int length, float frameRate) { -// morphAnimationBuffer = std::make_unique(data, numWeights, length / numWeights, 1000 / frameRate ); -// } - - // if(shaderPath) { - // opaqueShaderResources = _loadResource(opaqueShaderPath); - // fadeShaderResources = _loadResource(fadeShaderPath); - // _materialProvider = createGPUMorphShaderLoader( - // opaqueShaderResources.data, - // opaqueShaderResources.size, - // fadeShaderResources.data, - // fadeShaderResources.size, - // _engine); - // } else { - // } -// void printWeights(float* weights, int numWeights) { -// for(int i =0; i < numWeights; i++) { -// // std::cout << weights[i]; -// } -// } // void FilamentViewer::createMorpher(const char* meshName, int* primitives, int numPrimitives) { // // morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, primitives, numPrimitives); @@ -680,4 +649,25 @@ void FilamentViewer::updateViewportAndCameraProjection(int width, int height, fl // // xform = composeMatrix(translation + float3 { weights[0], weights[1], weights[2] }, rotation, scale ); // transformManager.setTransform(node, xform); +// } + +// void FilamentViewer::updateAnimation(AnimationBuffer animation, std::function callback) { +// if(morphAnimationBuffer.frameIndex >= animation.numFrames) { +// this.animation = null; +// return; +// } + +// if(animation.frameIndex == -1) { +// animation->frameIndex++; +// animation->lastTime = std::chrono::high_resolution_clock::now(); +// callback(); // applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); +// } else { +// duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime; +// float msElapsed = dur.count(); +// if(msElapsed > animation->frameLength) { +// animation->frameIndex++; +// animation->lastTime = std::chrono::high_resolution_clock::now(); +// callback(); // applyWeights(frameData + (frameIndex * numWeights), numWeights); +// } +// } // } \ No newline at end of file diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp index d4f00f82..71c19062 100644 --- a/ios/src/FilamentViewer.hpp +++ b/ios/src/FilamentViewer.hpp @@ -72,6 +72,23 @@ namespace polyvox { using LoadResource = std::function; using FreeResource = std::function; + struct MorphAnimationBuffer { + + MorphAnimationBuffer(float* frameData, + int numWeights, + int numFrames, + float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLength(frameLength) { + } + + int frameIndex = -1; + int numFrames; + float frameLength; + time_point_t startTime; + + float* frameData; + int numWeights; + }; + class FilamentViewer { public: FilamentViewer(void* layer, const char* opaqueShaderPath, const char* fadeShaderPath, LoadResource loadResource, FreeResource freeResource); @@ -87,7 +104,7 @@ namespace polyvox { StringList getTargetNames(const char* meshName); Manipulator* manipulator; void applyWeights(float* weights, int count); - // void animateWeights(float* data, int numWeights, int length, float frameRate); + void animateWeights(float* data, int numWeights, int length, float frameRate); // void animateBones(); void playAnimation(int index); bool setCamera(const char* nodeName); @@ -140,18 +157,20 @@ namespace polyvox { float _cameraFocalLength = 0.0f; + void updateMorphAnimation(); + // void updateEmbeddedAnimation(); + + // animation flags; + bool isAnimating; + unique_ptr morphAnimationBuffer; + // unique_ptr embeddedAnimationBuffer; + }; } - // void updateMorphAnimation(); - // void updateEmbeddedAnimation(); - // animation flags; - // bool isAnimating; - // unique_ptr morphAnimationBuffer; - // unique_ptr embeddedAnimationBuffer; // struct EmbeddedAnimationBuffer { @@ -162,19 +181,4 @@ namespace polyvox { // time_point_t lastTime; // }; - // struct MorphAnimationBuffer { - - // MorphAnimationBuffer(float* frameData, - // int numWeights, - // int numFrames, - // float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLength(frameLength) { - // } - - // int frameIndex = -1; - // int numFrames; - // float frameLength; - // time_point_t startTime; - - // float* frameData; - // int numWeights; - // }; \ No newline at end of file + \ No newline at end of file