diff --git a/example/assets/cube.glb b/example/assets/cube.glb index 9575871e..f211d1c0 100644 Binary files a/example/assets/cube.glb and b/example/assets/cube.glb differ diff --git a/example/lib/main.dart b/example/lib/main.dart index edb6854f..205a38fe 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -175,6 +175,49 @@ class _ExampleWidgetState extends State { _item(() async { _filamentController.clearAssets(); }, 'clear all assets'), + _item(() async { + var names = + await _filamentController.getMorphTargetNames(_cube!, "Cylinder"); + await showDialog( + context: context, + builder: (ctx) { + return Container( + height: 100, + width: 100, + color: Colors.white, + child: Text(names.join(","))); + }); + }, "show morph target names for Cylinder"), + _item(() { + _filamentController.setMorphTargetWeights( + _cube!, "Cylinder", List.filled(4, 1.0)); + }, "set Cylinder morph weights to 1"), + _item(() { + _filamentController.setMorphTargetWeights( + _cube!, "Cylinder", List.filled(4, 0.0)); + }, "set Cylinder morph weights to 0.0"), + _item(() async { + var morphs = + await _filamentController.getMorphTargetNames(_cube!, "Cylinder"); + final animation = AnimationBuilder( + availableMorphs: morphs, framerate: 30, meshName: "Cylinder") + .setDuration(4) + .setMorphTargets(["Key 1", "Key 2"]) + .interpolateMorphWeights(0, 4, 0, 1) + .build(); + _filamentController.setMorphAnimationData(_cube!, animation); + }, "animate morph weights #1 and #2"), + _item(() async { + var morphs = + await _filamentController.getMorphTargetNames(_cube!, "Cylinder"); + final animation = AnimationBuilder( + availableMorphs: morphs, framerate: 30, meshName: "Cylinder") + .setDuration(4) + .setMorphTargets(["Key 3", "Key 4"]) + .interpolateMorphWeights(0, 4, 0, 1) + .build(); + _filamentController.setMorphAnimationData(_cube!, animation); + }, "animate morph weights #3 and #4"), ]; if (_animations != null) { children.addAll(_animations!.map((a) => _item(() { @@ -273,14 +316,7 @@ class _ExampleWidgetState extends State { // _flightHelmet ??= await _filamentController.loadGltf( // 'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet'); // break; -// case 7: -// _filamentController.setMorphTargetWeights( -// _cube!, "Cube.001", List.filled(8, 1.0)); -// break; -// case 8: -// _filamentController.setMorphTargetWeights( -// _cube!, "Cube.001", List.filled(8, 0)); -// break; + // case 11: // setState(() { // _loop = !_loop; @@ -290,38 +326,6 @@ class _ExampleWidgetState extends State { // _filamentController.setCamera(_cube!, "Camera_Orientation"); // break; // case 15: -// final animation = AnimationBuilder( -// controller: _filamentController, -// asset: _cube!, -// framerate: 30, -// meshName: "Cube.001") -// .setDuration(4) -// .interpolateMorphWeights(0, 4, 0, 1) -// // .interpolateBoneTransform( -// // "Bone.001", -// // "Cube.001", -// // 2, -// // 4, -// // v.Vector3.zero(), -// // v.Vector3.zero(), -// // // Vec3(x: 1, y: 1, z: 1), -// // v.Quaternion(0, 0, 0, 1), -// // v.Quaternion(1, 1, 1, 1)) -// // Quaternion(x: 1, y: 1, z: 1, w: 1)) -// .set(); -// break; -// case 16: -// var names = -// await _filamentController.getMorphTargetNames(_cube!, "Cube"); -// await showDialog( -// context: context, -// builder: (ctx) { -// return Container( -// height: 100, -// width: 100, -// color: Colors.white, -// child: Text(names.join(","))); -// }); // break; // case 17: diff --git a/ios/Classes/SwiftPolyvoxFilamentPlugin.swift b/ios/Classes/SwiftPolyvoxFilamentPlugin.swift index b563fc88..9757bdb7 100644 --- a/ios/Classes/SwiftPolyvoxFilamentPlugin.swift +++ b/ios/Classes/SwiftPolyvoxFilamentPlugin.swift @@ -381,35 +381,41 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture let assetManager = args[0] as? Int64, let asset = args[1] as? EntityId, let entityName = args[2] as? String, - let morphData = args[3] as? FlutterStandardTypedData, - let numMorphWeights = args[4] as? Int else { + let morphData = args[3] as? [Double], + let numMorphWeights = args[4] as? Int32 else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for setMorphTargetWeights", details: nil)) return } - let success = morphData.data.withUnsafeBytes { buffer in - buffer.withMemoryRebound(to: Float.self) { - set_morph_target_weights(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, entityName, $0.baseAddress, Int32(numMorphWeights)) - } - } - result(success) + + set_morph_target_weights(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, entityName, morphData.map { Float($0) }, Int32(numMorphWeights)) + + result(true) case "setMorphAnimation": - guard let args = call.arguments as? [Any], args.count == 7, + guard let args = call.arguments as? [Any], args.count == 8, let assetManager = args[0] as? Int64, let asset = args[1] as? EntityId, let entityName = args[2] as? String, - let morphData = args[3] as? FlutterStandardTypedData, - let numMorphWeights = args[4] as? Int, - let numFrames = args[5] as? Int, - let frameLengthInMs = args[6] as? Double else { - result(FlutterError(code: "INVALID_ARGUMENTS", message: "Expected correct arguments for set_morph_animation", details: nil)) + let morphData = args[3] as? [Double], + let morphIndices = args[4] as? [Int32], + let numMorphTargets = args[5] as? Int32, + let numFrames = args[6] as? Int32, + let frameLengthInMs = args[7] as? Double else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: "Incorrect arguments provided for setMorphAnimation", details: nil)) return } - let success = morphData.data.withUnsafeBytes { buffer in - buffer.withMemoryRebound(to: Float.self) { - set_morph_animation(unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self), asset, entityName, $0.baseAddress, Int32(numMorphWeights), Int32(numFrames), Float(frameLengthInMs)) - } - } + let frameData = morphData.map { Float($0) } + let am = unsafeBitCast(assetManager, to:UnsafeMutableRawPointer.self) + + let success = set_morph_animation( + am, + asset, + entityName, + frameData, + morphIndices, + Int32(numMorphTargets), + Int32(numFrames), + Float(frameLengthInMs)) result(success) case "setBoneAnimation": guard let args = call.arguments as? [Any], args.count == 9, diff --git a/ios/include/AssetManager.hpp b/ios/include/AssetManager.hpp index 3dfa3778..ae20a260 100644 --- a/ios/include/AssetManager.hpp +++ b/ios/include/AssetManager.hpp @@ -45,9 +45,11 @@ namespace polyvox { EntityId entityId, const char* entityName, const float* const morphData, - int numMorphWeights, + const int* const morphIndices, + int numMorphTargets, int numFrames, float frameLengthInMs); + void setMorphTargetWeights(EntityId entityId, const char* const entityName, const float* const weights, int count); bool setBoneAnimationBuffer( diff --git a/ios/include/PolyvoxFilamentApi.h b/ios/include/PolyvoxFilamentApi.h index 36749e9c..06a248b5 100644 --- a/ios/include/PolyvoxFilamentApi.h +++ b/ios/include/PolyvoxFilamentApi.h @@ -54,9 +54,11 @@ bool set_morph_animation( EntityId asset, const char *const entityName, const float *const morphData, - int numMorphWeights, + const int* const morphIndices, + int numMorphTargets, int numFrames, float frameLengthInMs); + void set_bone_animation( void* assetManager, EntityId asset, diff --git a/ios/include/SceneAsset.hpp b/ios/include/SceneAsset.hpp index c5e1025f..8957fe85 100644 --- a/ios/include/SceneAsset.hpp +++ b/ios/include/SceneAsset.hpp @@ -52,7 +52,7 @@ namespace polyvox { int mNumFrames = -1; float mFrameLengthInMs = 0; vector mFrameData; - int mNumMorphWeights = 0; + vector mMorphIndices; }; // diff --git a/ios/src/AssetManager.cpp b/ios/src/AssetManager.cpp index 2a781c92..34e7963e 100644 --- a/ios/src/AssetManager.cpp +++ b/ios/src/AssetManager.cpp @@ -272,12 +272,16 @@ void AssetManager::updateAnimations() { if(anim.mReverse) { frameNumber = lengthInFrames - frameNumber; } - // set the weights appropriately - rm.setMorphWeights( - rm.getInstance(asset.mMorphAnimationBuffer.mMeshTarget), - asset.mMorphAnimationBuffer.mFrameData.data() + (frameNumber * asset.mMorphAnimationBuffer.mNumMorphWeights), - asset.mMorphAnimationBuffer.mNumMorphWeights - ); + auto baseOffset = frameNumber * asset.mMorphAnimationBuffer.mMorphIndices.size(); + for(auto morphIndex : asset.mMorphAnimationBuffer.mMorphIndices) { + // set the weights appropriately + rm.setMorphWeights( + rm.getInstance(asset.mMorphAnimationBuffer.mMeshTarget), + asset.mMorphAnimationBuffer.mFrameData.data() + baseOffset + morphIndex, + 1, + morphIndex + ); + } break; } case AnimationType::BONE: { @@ -416,7 +420,8 @@ bool AssetManager::setMorphAnimationBuffer( EntityId entityId, const char* entityName, const float* const morphData, - int numMorphWeights, + const int* const morphIndices, + int numMorphTargets, int numFrames, float frameLengthInMs) { @@ -438,10 +443,13 @@ bool AssetManager::setMorphAnimationBuffer( asset.mMorphAnimationBuffer.mFrameData.insert( asset.mMorphAnimationBuffer.mFrameData.begin(), morphData, - morphData + (numFrames * numMorphWeights) + morphData + (numFrames * numMorphTargets) ); asset.mMorphAnimationBuffer.mFrameLengthInMs = frameLengthInMs; - asset.mMorphAnimationBuffer.mNumMorphWeights = numMorphWeights; + asset.mMorphAnimationBuffer.mMorphIndices.resize(numMorphTargets); + for(int i =0; i< numMorphTargets; i++) { + asset.mMorphAnimationBuffer.mMorphIndices[i] = morphIndices[i]; + } AnimationStatus animation; animation.mDuration = (frameLengthInMs * numFrames) / 1000.0f; diff --git a/ios/src/PolyvoxFilamentApi.cpp b/ios/src/PolyvoxFilamentApi.cpp index ed28f381..1d4062de 100644 --- a/ios/src/PolyvoxFilamentApi.cpp +++ b/ios/src/PolyvoxFilamentApi.cpp @@ -196,7 +196,8 @@ extern "C" { EntityId asset, const char* const entityName, const float* const morphData, - int numMorphWeights, + const int* const morphIndices, + int numMorphTargets, int numFrames, float frameLengthInMs) { @@ -204,7 +205,8 @@ extern "C" { asset, entityName, morphData, - numMorphWeights, + morphIndices, + numMorphTargets, numFrames, frameLengthInMs ); diff --git a/lib/animations/animation_builder.dart b/lib/animations/animation_builder.dart index 969b9c82..37e9548d 100644 --- a/lib/animations/animation_builder.dart +++ b/lib/animations/animation_builder.dart @@ -1,4 +1,3 @@ -import 'package:polyvox_filament/animations/bone_animation_data.dart'; import 'package:polyvox_filament/animations/morph_animation_data.dart'; import 'package:polyvox_filament/filament_controller.dart'; @@ -6,7 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:vector_math/vector_math.dart'; class AnimationBuilder { - final FilamentController controller; // BoneAnimationData? BoneAnimationData; double _frameLengthInMs = 0; double _duration = 0; @@ -18,28 +16,25 @@ class AnimationBuilder { // List? _BoneAnimationDatas = null; - FilamentEntity asset; String meshName; - late List morphNames; + late List availableMorphs; + late List _morphTargets; AnimationBuilder( - {required this.controller, - required this.asset, + {required this.availableMorphs, required this.meshName, required int framerate}) { _frameLengthInMs = 1000 / framerate; - controller.getMorphTargetNames(asset, meshName).then((value) { - morphNames = value; - }); } - void set() { - if (morphNames.isEmpty == 0 || _duration == 0 || _frameLengthInMs == 0) + MorphAnimationData build() { + if (availableMorphs.isEmpty == 0 || _duration == 0 || _frameLengthInMs == 0) throw Exception(); int numFrames = _duration * 1000 ~/ _frameLengthInMs; - final morphData = Float32List((numFrames * morphNames.length).toInt()); + final morphData = + List.filled((numFrames * _morphTargets.length).toInt(), 0.0); var frameStart = (_interpMorphStart! * 1000) ~/ _frameLengthInMs; var frameEnd = (_interpMorphEnd! * 1000) ~/ _frameLengthInMs; @@ -49,17 +44,16 @@ class AnimationBuilder { var val = ((1 - linear) * _interpMorphStartValue!) + (linear * _interpMorphEndValue!); - for (int j = 0; j < morphNames.length; j++) { - morphData[(i * morphNames.length) + j] = val; + for (int j = 0; j < _morphTargets.length; j++) { + morphData[(i * _morphTargets.length) + j] = val; } } - - var morphAnimation = - MorphAnimationData(meshName, morphData, morphNames, _frameLengthInMs); - - controller.setMorphAnimationData(asset, morphAnimation); - // return Tuple2>( - // morphAnimation, _BoneAnimationDatas!); + return MorphAnimationData( + meshName, + morphData, + _morphTargets.map((i) => availableMorphs[i]).toList(), + _morphTargets, + _frameLengthInMs); } AnimationBuilder setDuration(double secs) { @@ -67,6 +61,11 @@ class AnimationBuilder { return this; } + AnimationBuilder setMorphTargets(List names) { + _morphTargets = names.map((name) => availableMorphs.indexOf(name)).toList(); + return this; + } + AnimationBuilder interpolateMorphWeights( double start, double end, double startValue, double endValue) { this._interpMorphStart = start; diff --git a/lib/animations/csv_animation.dart b/lib/animations/csv_animation.dart index 5de360a3..09374ed2 100644 --- a/lib/animations/csv_animation.dart +++ b/lib/animations/csv_animation.dart @@ -32,7 +32,11 @@ class DynamicAnimation { } var morphAnimationData = MorphAnimationData( - meshName ?? "NULL", llf.item2, morphNames, frameLengthInMs); + meshName ?? "NULL", + llf.item2, + morphNames, + List.generate(morphNames.length, (index) => index), + frameLengthInMs); final boneAnimations = []; diff --git a/lib/animations/morph_animation_data.dart b/lib/animations/morph_animation_data.dart index ad7ce936..30eb0320 100644 --- a/lib/animations/morph_animation_data.dart +++ b/lib/animations/morph_animation_data.dart @@ -9,24 +9,25 @@ import 'dart:typed_data'; class MorphAnimationData { final String meshName; final List morphNames; + final List morphIndices; - final Float32List data; + final List data; - MorphAnimationData( - this.meshName, this.data, this.morphNames, this.frameLengthInMs) { + MorphAnimationData(this.meshName, this.data, this.morphNames, + this.morphIndices, this.frameLengthInMs) { assert(data.length == morphNames.length * numFrames); } - int get numMorphWeights => morphNames.length; + int get numMorphTargets => morphNames.length; - int get numFrames => data.length ~/ numMorphWeights; + int get numFrames => data.length ~/ numMorphTargets; final double frameLengthInMs; Iterable getData(String morphName) sync* { int index = morphNames.indexOf(morphName); for (int i = 0; i < numFrames; i++) { - yield data[(i * numMorphWeights) + index]; + yield data[(i * numMorphTargets) + index]; } } } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 83b32ba0..dca93c3d 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -260,12 +260,14 @@ class FilamentController { /// void setMorphAnimationData( FilamentEntity asset, MorphAnimationData animation) async { + print("SETTING animation with ${animation.data}"); await _channel.invokeMethod("setMorphAnimation", [ _assetManager, asset, animation.meshName, animation.data, - animation.numMorphWeights, + animation.morphIndices, + animation.numMorphTargets, animation.numFrames, animation.frameLengthInMs ]); diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index db1e4458..a1207687 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -450,21 +450,6 @@ class NativeLibrary { late final _set_frame_interval = _set_frame_intervalPtr .asFunction, double)>(); - ffi.Pointer get_renderer( - ffi.Pointer viewer, - ) { - return _get_renderer( - viewer, - ); - } - - late final _get_rendererPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('get_renderer'); - late final _get_renderer = _get_rendererPtr - .asFunction Function(ffi.Pointer)>(); - void update_viewport_and_camera_projection( ffi.Pointer viewer, int width, @@ -652,7 +637,8 @@ class NativeLibrary { int asset, ffi.Pointer entityName, ffi.Pointer morphData, - int numMorphWeights, + ffi.Pointer morphIndices, + int numMorphTargets, int numFrames, double frameLengthInMs, ) { @@ -661,7 +647,8 @@ class NativeLibrary { asset, entityName, morphData, - numMorphWeights, + morphIndices, + numMorphTargets, numFrames, frameLengthInMs, ); @@ -674,12 +661,13 @@ class NativeLibrary { EntityId, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Int, ffi.Int, ffi.Float)>>('set_morph_animation'); late final _set_morph_animation = _set_morph_animationPtr.asFunction< int Function(ffi.Pointer, int, ffi.Pointer, - ffi.Pointer, int, int, double)>(); + ffi.Pointer, ffi.Pointer, int, int, double)>(); void set_bone_animation( ffi.Pointer assetManager, @@ -735,6 +723,7 @@ class NativeLibrary { int index, int loop, int reverse, + int replaceActive, double crossfade, ) { return _play_animation( @@ -743,6 +732,7 @@ class NativeLibrary { index, loop, reverse, + replaceActive, crossfade, ); } @@ -750,9 +740,9 @@ class NativeLibrary { late final _play_animationPtr = _lookup< ffi.NativeFunction< ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int, - ffi.Int, ffi.Float)>>('play_animation'); + ffi.Int, ffi.Int, ffi.Float)>>('play_animation'); late final _play_animation = _play_animationPtr.asFunction< - void Function(ffi.Pointer, int, int, int, int, double)>(); + void Function(ffi.Pointer, int, int, int, int, int, double)>(); void set_animation_frame( ffi.Pointer assetManager, @@ -832,6 +822,25 @@ class NativeLibrary { late final _get_animation_name = _get_animation_namePtr.asFunction< void Function(ffi.Pointer, int, ffi.Pointer, int)>(); + double get_animation_duration( + ffi.Pointer assetManager, + int asset, + int index, + ) { + return _get_animation_duration( + assetManager, + asset, + index, + ); + } + + late final _get_animation_durationPtr = _lookup< + ffi.NativeFunction< + ffi.Float Function(ffi.Pointer, EntityId, + ffi.Int)>>('get_animation_duration'); + late final _get_animation_duration = _get_animation_durationPtr + .asFunction, int, int)>(); + void get_morph_target_name( ffi.Pointer assetManager, int asset, @@ -1195,7 +1204,7 @@ class NativeLibrary { late final _ios_dummy = _ios_dummyPtr.asFunction(); } -final class __mbstate_t extends ffi.Union { +class __mbstate_t extends ffi.Union { @ffi.Array.multi([128]) external ffi.Array __mbstate8; @@ -1203,7 +1212,7 @@ final class __mbstate_t extends ffi.Union { external int _mbstateL; } -final class __darwin_pthread_handler_rec extends ffi.Struct { +class __darwin_pthread_handler_rec extends ffi.Struct { external ffi .Pointer)>> __routine; @@ -1213,7 +1222,7 @@ final class __darwin_pthread_handler_rec extends ffi.Struct { external ffi.Pointer<__darwin_pthread_handler_rec> __next; } -final class _opaque_pthread_attr_t extends ffi.Struct { +class _opaque_pthread_attr_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1221,7 +1230,7 @@ final class _opaque_pthread_attr_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_cond_t extends ffi.Struct { +class _opaque_pthread_cond_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1229,7 +1238,7 @@ final class _opaque_pthread_cond_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_condattr_t extends ffi.Struct { +class _opaque_pthread_condattr_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1237,7 +1246,7 @@ final class _opaque_pthread_condattr_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_mutex_t extends ffi.Struct { +class _opaque_pthread_mutex_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1245,7 +1254,7 @@ final class _opaque_pthread_mutex_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_mutexattr_t extends ffi.Struct { +class _opaque_pthread_mutexattr_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1253,7 +1262,7 @@ final class _opaque_pthread_mutexattr_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_once_t extends ffi.Struct { +class _opaque_pthread_once_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1261,7 +1270,7 @@ final class _opaque_pthread_once_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_rwlock_t extends ffi.Struct { +class _opaque_pthread_rwlock_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1269,7 +1278,7 @@ final class _opaque_pthread_rwlock_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_rwlockattr_t extends ffi.Struct { +class _opaque_pthread_rwlockattr_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1277,7 +1286,7 @@ final class _opaque_pthread_rwlockattr_t extends ffi.Struct { external ffi.Array __opaque; } -final class _opaque_pthread_t extends ffi.Struct { +class _opaque_pthread_t extends ffi.Struct { @ffi.Long() external int __sig; @@ -1287,7 +1296,7 @@ final class _opaque_pthread_t extends ffi.Struct { external ffi.Array __opaque; } -final class ResourceBuffer extends ffi.Struct { +class ResourceBuffer extends ffi.Struct { external ffi.Pointer data; @ffi.Uint32() @@ -1297,7 +1306,7 @@ final class ResourceBuffer extends ffi.Struct { external int id; } -final class ResourceLoaderWrapper extends ffi.Struct { +class ResourceLoaderWrapper extends ffi.Struct { external LoadResource mLoadResource; external FreeResource mFreeResource;