add support for multiple bone animations using quaternions

This commit is contained in:
Nick Fisher
2023-11-17 23:44:30 +08:00
parent f5d5a36f22
commit 572a945025
13 changed files with 1266 additions and 1254 deletions

View File

@@ -97,6 +97,8 @@ class ExampleWidgetState extends State<ExampleWidget> {
await _filamentController!.setRendering(true); await _filamentController!.setRendering(true);
shapes = shapes =
await _filamentController!.loadGlb("assets/shapes/shapes.glb"); await _filamentController!.loadGlb("assets/shapes/shapes.glb");
ExampleWidgetState.animations = await _filamentController!
.getAnimationNames(ExampleWidgetState.shapes!);
hasSkybox = true; hasSkybox = true;
rendering = true; rendering = true;
}); });

View File

@@ -2,10 +2,13 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_filament/animations/animation_data.dart';
import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament_example/main.dart'; import 'package:flutter_filament_example/main.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class AssetSubmenu extends StatefulWidget { class AssetSubmenu extends StatefulWidget {
final FilamentController controller; final FilamentController controller;
const AssetSubmenu({super.key, required this.controller}); const AssetSubmenu({super.key, required this.controller});
@@ -65,7 +68,25 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
0, 0,
Matrix4.rotationX(pi / 2)); Matrix4.rotationX(pi / 2));
}, },
child: const Text('Set bone tranform to identity for Cylinder')), child:
const Text('Set bone transform for Cylinder (pi/2 rotation X)')),
MenuItemButton(
onPressed: ExampleWidgetState.shapes == null
? null
: () async {
await widget.controller.addBoneAnimation(
ExampleWidgetState.shapes!,
BoneAnimationData(
"Bone",
["Cylinder"],
List.generate(
60,
(idx) => v.Quaternion.axisAngle(
v.Vector3(0, 0, 1), pi * (idx / 60))
.normalized()),
1000.0 / 30.0));
},
child: const Text('Set bone transform animation for Cylinder')),
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
var names = await widget.controller var names = await widget.controller

View File

@@ -13,11 +13,13 @@
typedef int32_t EntityId; typedef int32_t EntityId;
namespace polyvox { namespace polyvox
{
using namespace filament; using namespace filament;
using namespace filament::gltfio; using namespace filament::gltfio;
class AssetManager { class AssetManager
{
public: public:
AssetManager(const ResourceLoaderWrapper *const loader, AssetManager(const ResourceLoaderWrapper *const loader,
NameComponentManager *ncm, NameComponentManager *ncm,
@@ -56,12 +58,29 @@ namespace polyvox {
void setMorphTargetWeights(EntityId entityId, const char *const entityName, const float *const weights, int count); void setMorphTargetWeights(EntityId entityId, const char *const entityName, const float *const weights, int count);
bool setBoneAnimationBuffer( /// @brief Set the local transform for the bone at boneIndex/skinIndex in the given entity.
/// @param entityId
/// @param entityName
/// @param skinIndex
/// @param boneIndex
/// @param transform
/// @return
bool setBoneTransform(EntityId entityId, const char *entityName, int skinIndex, int boneIndex, math::mat4f transform);
/// @brief Set frame data to animate the given bones/entities.
/// @param entity
/// @param frameData frame data as quaternions
/// @param numFrames
/// @param boneName
/// @param meshName
/// @param numMeshTargets
/// @param frameLengthInMs
/// @return
bool addBoneAnimation(
EntityId entity, EntityId entity,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
int numBones, const char *const boneName,
const char** const boneNames,
const char **const meshName, const char **const meshName,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs); float frameLengthInMs);
@@ -74,8 +93,6 @@ namespace polyvox {
bool reveal(EntityId entity, const char *meshName); bool reveal(EntityId entity, const char *meshName);
const char *getNameForEntity(EntityId entityId); const char *getNameForEntity(EntityId entityId);
bool setBoneTransform(EntityId entityId, const char* entityName, int skinIndex, int boneIndex, math::mat4f transform);
private: private:
AssetLoader *_assetLoader = nullptr; AssetLoader *_assetLoader = nullptr;
const ResourceLoaderWrapper *const _resourceLoaderWrapper; const ResourceLoaderWrapper *const _resourceLoaderWrapper;
@@ -93,12 +110,11 @@ namespace polyvox {
utils::Entity findEntityByName( utils::Entity findEntityByName(
SceneAsset asset, SceneAsset asset,
const char* entityName const char *entityName);
);
inline void updateTransform(SceneAsset &asset); inline void updateTransform(SceneAsset &asset);
inline void setBoneTransformFromAnimation(SceneAsset& asset, int frameNumber); void updateBoneTransformFromAnimationBuffer(const BoneAnimation& animation, int frameNumber);
}; };
} }

View File

@@ -50,7 +50,8 @@ typedef int32_t EntityId;
typedef int32_t _ManipulatorMode; typedef int32_t _ManipulatorMode;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C"
{
#endif #endif
FLUTTER_PLUGIN_EXPORT const void *create_filament_viewer(const void *const context, const ResourceLoaderWrapper *const loader, void *const platform, const char *uberArchivePath); FLUTTER_PLUGIN_EXPORT const void *create_filament_viewer(const void *const context, const ResourceLoaderWrapper *const loader, void *const platform, const char *uberArchivePath);
@@ -80,8 +81,7 @@ FLUTTER_PLUGIN_EXPORT void render(
uint64_t frameTimeInNanos, uint64_t frameTimeInNanos,
void *pixelBuffer, void *pixelBuffer,
void (*callback)(void *buf, size_t size, void *data), void (*callback)(void *buf, size_t size, void *data),
void* data void *data);
);
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void *const viewer, const void *const window, uint32_t width, uint32_t height); FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void *const viewer, const void *const window, uint32_t width, uint32_t height);
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void *const viewer); FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(const void *const viewer);
FLUTTER_PLUGIN_EXPORT void set_frame_interval(const void *const viewer, float interval); FLUTTER_PLUGIN_EXPORT void set_frame_interval(const void *const viewer, float interval);
@@ -97,15 +97,13 @@ FLUTTER_PLUGIN_EXPORT void apply_weights(
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
float *const weights, float *const weights,
int count int count);
);
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights( FLUTTER_PLUGIN_EXPORT void set_morph_target_weights(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
const float *const morphData, const float *const morphData,
int numWeights int numWeights);
);
FLUTTER_PLUGIN_EXPORT bool set_morph_animation( FLUTTER_PLUGIN_EXPORT bool set_morph_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
@@ -115,14 +113,13 @@ FLUTTER_PLUGIN_EXPORT bool set_morph_animation(
int numMorphTargets, int numMorphTargets,
int numFrames, int numFrames,
float frameLengthInMs); float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT void set_bone_animation( FLUTTER_PLUGIN_EXPORT void add_bone_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
int numBones, const char *const boneName,
const char** const boneNames, const char **const meshNames,
const char** const meshName,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs); float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT bool set_bone_transform( FLUTTER_PLUGIN_EXPORT bool set_bone_transform(
@@ -130,8 +127,7 @@ FLUTTER_PLUGIN_EXPORT bool set_bone_transform(
EntityId asset, EntityId asset,
const char *entityName, const char *entityName,
const float *const transform, const float *const transform,
int boneIndex int boneIndex);
);
FLUTTER_PLUGIN_EXPORT void play_animation(void *assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); FLUTTER_PLUGIN_EXPORT void play_animation(void *assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade);
FLUTTER_PLUGIN_EXPORT void set_animation_frame(void *assetManager, EntityId asset, int animationIndex, int animationFrame); FLUTTER_PLUGIN_EXPORT void set_animation_frame(void *assetManager, EntityId asset, int animationIndex, int animationFrame);
FLUTTER_PLUGIN_EXPORT void stop_animation(void *assetManager, EntityId asset, int index); FLUTTER_PLUGIN_EXPORT void stop_animation(void *assetManager, EntityId asset, int index);
@@ -167,7 +163,6 @@ FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, flo
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void *const viewer, float focusDistance); FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void *const viewer, float focusDistance);
FLUTTER_PLUGIN_EXPORT void set_camera_manipulator_options(const void *const viewer, _ManipulatorMode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed); FLUTTER_PLUGIN_EXPORT void set_camera_manipulator_options(const void *const viewer, _ManipulatorMode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed);
FLUTTER_PLUGIN_EXPORT int hide_mesh(void *assetManager, EntityId asset, const char *meshName); FLUTTER_PLUGIN_EXPORT int hide_mesh(void *assetManager, EntityId asset, const char *meshName);
FLUTTER_PLUGIN_EXPORT int reveal_mesh(void *assetManager, EntityId asset, const char *meshName); FLUTTER_PLUGIN_EXPORT int reveal_mesh(void *assetManager, EntityId asset, const char *meshName);
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled);

View File

@@ -66,17 +66,6 @@ FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi(
int numMorphTargets, int numMorphTargets,
int numFrames, int numFrames,
float frameLengthInMs); float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT
FLUTTER_PLUGIN_EXPORT void set_bone_animation_ffi(
void* const assetManager,
EntityId asset,
const float* const frameData,
int numFrames,
int numBones,
const char** const boneNames,
const char** const meshName,
int numMeshTargets,
float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade);
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const assetManager, EntityId asset, int animationIndex, int animationFrame); FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const assetManager, EntityId asset, int animationIndex, int animationFrame);
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const assetManager, EntityId asset, int index); FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const assetManager, EntityId asset, int index);

View File

@@ -36,50 +36,46 @@ namespace polyvox {
}; };
struct AnimationStatus { struct AnimationStatus {
time_point_t mStart = time_point_t::max(); time_point_t start = time_point_t::max();
bool mLoop = false; bool loop = false;
bool mReverse = false; bool reverse = false;
float mDuration = 0; float durationInSecs = 0;
AnimationType type; };
int gltfIndex = -1;
struct GltfAnimation : AnimationStatus {
int index = -1;
}; };
// //
// Use this to manually construct a buffer of frame data for morph animations. // Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation.
// //
struct MorphAnimationBuffer { struct MorphAnimation : AnimationStatus {
utils::Entity mMeshTarget; utils::Entity meshTarget;
int mNumFrames = -1; int numFrames = -1;
float mFrameLengthInMs = 0; float frameLengthInMs = 0;
vector<float> mFrameData; vector<float> frameData;
vector<int> mMorphIndices; vector<int> morphIndices;
int lengthInFrames;
}; };
// //
// Use this to construct a dynamic (i.e. non-glTF embedded) bone animation. // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation.
// Only a single animation is supported at any time (i.e you can't blend animations).
// Multiple bones are supported but these must be skinned to a single mesh target.
// //
struct BoneAnimationBuffer { struct BoneAnimation : AnimationStatus {
vector<utils::Entity> mMeshTargets; uint8_t boneIndex;
vector<uint8_t> mBones; vector<utils::Entity> meshTargets;
vector<math::mat4f> mBaseTransforms;
// vector<math::float3> mBaseTranslations; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
// vector<math::quatf> mBaseRotations; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
// vector<math::float3> mBaseScales; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
size_t skinIndex = 0; size_t skinIndex = 0;
int mNumFrames = -1; int lengthInFrames;
float mFrameLengthInMs = 0; float frameLengthInMs = 0;
vector<float> mFrameData; vector<const math::quatf> frameData;
}; };
struct SceneAsset { struct SceneAsset {
bool mAnimating = false; FilamentAsset* asset = nullptr;
FilamentAsset* mAsset = nullptr;
Animator* mAnimator = nullptr;
// vector containing AnimationStatus structs for the morph, bone and/or glTF animations. vector<GltfAnimation> gltfAnimations;
vector<AnimationStatus> mAnimations; vector<MorphAnimation> morphAnimations;
vector<BoneAnimation> boneAnimations;
// the index of the last active glTF animation, // the index of the last active glTF animation,
// used to cross-fade // used to cross-fade
@@ -87,24 +83,19 @@ namespace polyvox {
float fadeDuration = 0.0f; float fadeDuration = 0.0f;
float fadeOutAnimationStart = 0.0f; float fadeOutAnimationStart = 0.0f;
MorphAnimationBuffer mMorphAnimationBuffer;
BoneAnimationBuffer mBoneAnimationBuffer;
// a slot to preload textures // a slot to preload textures
filament::Texture* mTexture = nullptr; filament::Texture* texture = nullptr;
// initialized to identity // initialized to identity
math::mat4f mPosition; math::mat4f position;
// initialized to identity // initialized to identity
math::mat4f mRotation; math::mat4f rotation;
float mScale = 1; float mScale = 1;
SceneAsset( SceneAsset(
FilamentAsset* asset FilamentAsset* asset
) : mAsset(asset) { ) : asset(asset) {}
mAnimator = mAsset->getInstance()->getAnimator();
}
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@@ -321,18 +321,17 @@ extern "C"
return ((AssetManager *)assetManager)->setMorphAnimationBuffer(asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); return ((AssetManager *)assetManager)->setMorphAnimationBuffer(asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
} }
FLUTTER_PLUGIN_EXPORT void set_bone_animation( FLUTTER_PLUGIN_EXPORT void add_bone_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
int numBones, const char *const boneName,
const char **const boneNames,
const char **const meshNames, const char **const meshNames,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs) float frameLengthInMs)
{ {
((AssetManager *)assetManager)->setBoneAnimationBuffer(asset, frameData, numFrames, numBones, boneNames, meshNames, numMeshTargets, frameLengthInMs); ((AssetManager *)assetManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs);
} }
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled) FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled)
@@ -366,33 +365,6 @@ extern "C"
return ((AssetManager *)assetManager)->setBoneTransform(entityId, entityName, 0, boneIndex, matrix); return ((AssetManager *)assetManager)->setBoneTransform(entityId, entityName, 0, boneIndex, matrix);
} }
// void set_bone_transform(
// EntityId asset,
// const char* boneName,
// const char* entityName,
// float transX,
// float transY,
// float transZ,
// float quatX,
// float quatY,
// float quatZ,
// float quatW
// ) {
// ((AssetManager*)assetManager)->setBoneTransform(
// boneName,
// entityName,
// transX,
// transY,
// transZ,
// quatX,
// quatY,
// quatZ,
// quatW,
// false
// );
// }
FLUTTER_PLUGIN_EXPORT void play_animation( FLUTTER_PLUGIN_EXPORT void play_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,

View File

@@ -331,18 +331,6 @@ FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void *const viewer, EntityId asset,
return fut.get(); return fut.get();
} }
FLUTTER_PLUGIN_EXPORT void set_bone_animation_ffi(
void *assetManager, EntityId asset, const float *const frameData,
int numFrames, int numBones, const char **const boneNames,
const char **const meshName, int numMeshTargets, float frameLengthInMs) {
std::packaged_task<void()> lambda([&] {
set_bone_animation(assetManager, asset, frameData, numFrames, numBones,
boneNames, meshName, numMeshTargets, frameLengthInMs);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_ffi(void *assetManager, EntityId asset, get_morph_target_name_ffi(void *assetManager, EntityId asset,
const char *meshName, char *const outPtr, int index) { const char *meshName, char *const outPtr, int index) {

View File

@@ -1,5 +1,7 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
/// ///
/// Specifies frame data (i.e. weights) to animate the morph targets contained in [morphTargets] under a mesh named [mesh]. /// Specifies frame data (i.e. weights) to animate the morph targets contained in [morphTargets] under a mesh named [mesh].
/// [data] is laid out as numFrames x numMorphTargets. /// [data] is laid out as numFrames x numMorphTargets.
@@ -40,7 +42,7 @@ class MorphAnimationData {
class BoneAnimationData { class BoneAnimationData {
final String boneName; final String boneName;
final List<String> meshNames; final List<String> meshNames;
final Float32List frameData; final List<Quaternion> frameData;
double frameLengthInMs; double frameLengthInMs;
BoneAnimationData( BoneAnimationData(
this.boneName, this.meshNames, this.frameData, this.frameLengthInMs); this.boneName, this.meshNames, this.frameData, this.frameLengthInMs);

View File

@@ -272,12 +272,9 @@ abstract class FilamentController {
FilamentEntity entity, MorphAnimationData animation); FilamentEntity entity, MorphAnimationData animation);
/// ///
/// Animates morph target weights/bone transforms (where each frame requires a duration of [frameLengthInMs]. /// Starts animating a bone (joint) according to the specified [animation].
/// [morphWeights] is a list of doubles in frame-major format.
/// Each frame is [numWeights] in length, and each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
/// for now we only allow animating a single bone (though multiple skinned targets are supported)
/// ///
Future setBoneAnimation(FilamentEntity entity, BoneAnimationData animation); Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation);
/// ///
/// Sets the local joint transform for the bone at the given index in [entity] for the mesh under [meshName]. /// Sets the local joint transform for the bone at the given index in [entity] for the mesh under [meshName].

View File

@@ -691,41 +691,40 @@ class FilamentControllerFFI extends FilamentController {
} }
@override @override
Future setBoneAnimation( Future addBoneAnimation(
FilamentEntity entity, BoneAnimationData animation) async { FilamentEntity entity, BoneAnimationData animation) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
} }
// var data = calloc<Float>(animation.frameData.length);
// int offset = 0;
// var numFrames = animation.frameData.length ~/ 7;
// var boneNames = calloc<Pointer<Char>>(1);
// boneNames.elementAt(0).value =
// animation.boneName.toNativeUtf8().cast<Char>();
// var meshNames = calloc<Pointer<Char>>(animation.meshNames.length); var numFrames = animation.frameData.length;
// for (int i = 0; i < animation.meshNames.length; i++) {
// meshNames.elementAt(i).value =
// animation.meshNames[i].toNativeUtf8().cast<Char>();
// }
// for (int i = 0; i < animation.frameData.length; i++) { var meshNames = calloc<Pointer<Char>>(animation.meshNames.length);
// data.elementAt(offset).value = animation.frameData[i]; for (int i = 0; i < animation.meshNames.length; i++) {
// offset += 1; meshNames.elementAt(i).value =
// } animation.meshNames[i].toNativeUtf8().cast<Char>();
}
// await _channel.invokeMethod("setBoneAnimation", [ var data = calloc<Float>(numFrames * 4);
// _assetManager!,
// asset, for (int i = 0; i < numFrames; i++) {
// data, data.elementAt(i * 4).value = animation.frameData[i].w;
// numFrames, data.elementAt((i * 4) + 1).value = animation.frameData[i].x;
// 1, data.elementAt((i * 4) + 2).value = animation.frameData[i].y;
// boneNames, data.elementAt((i * 4) + 3).value = animation.frameData[i].z;
// meshNames, }
// animation.meshNames.length,
// animation.frameLengthInMs add_bone_animation(
// ]); _assetManager!,
// calloc.free(data); entity,
data,
numFrames,
animation.boneName.toNativeUtf8().cast<Char>(),
meshNames,
animation.meshNames.length,
animation.frameLengthInMs);
calloc.free(data);
calloc.free(meshNames);
} }
@override @override

View File

@@ -370,20 +370,18 @@ external bool set_morph_animation(
EntityId, EntityId,
ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Float>,
ffi.Int, ffi.Int,
ffi.Int, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Pointer<ffi.Pointer<ffi.Char>>, ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Int, ffi.Int,
ffi.Float)>( ffi.Float)>(
symbol: 'set_bone_animation', assetId: 'flutter_filament_plugin') symbol: 'add_bone_animation', assetId: 'flutter_filament_plugin')
external void set_bone_animation( external void add_bone_animation(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> assetManager,
int asset, int asset,
ffi.Pointer<ffi.Float> frameData, ffi.Pointer<ffi.Float> frameData,
int numFrames, int numFrames,
int numBones, ffi.Pointer<ffi.Char> boneName,
ffi.Pointer<ffi.Pointer<ffi.Char>> boneNames, ffi.Pointer<ffi.Pointer<ffi.Char>> meshNames,
ffi.Pointer<ffi.Pointer<ffi.Char>> meshName,
int numMeshTargets, int numMeshTargets,
double frameLengthInMs, double frameLengthInMs,
); );