split animation components into GltfAnimation/MorphAnimation/BoneAnimation

This commit is contained in:
Nick Fisher
2025-05-17 10:11:55 +08:00
parent f9d09e17ef
commit c98e604e76
12 changed files with 1109 additions and 483 deletions

View File

@@ -2935,20 +2935,52 @@ external void AnimationManager_update(
int frameTimeInNanos,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
external bool AnimationManager_addGltfAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> tSceneAsset,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
external bool AnimationManager_removeGltfAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> tSceneAsset,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
isLeaf: true)
external void AnimationManager_addAnimationComponent(
external void AnimationManager_addMorphAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
int entityId,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
isLeaf: true)
external void AnimationManager_removeAnimationComponent(
external void AnimationManager_removeMorphAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
int entityId,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
external bool AnimationManager_addBoneAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> tSceneAsset,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
external bool AnimationManager_removeBoneAnimationComponent(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> tSceneAsset,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TAnimationManager>,
@@ -2984,7 +3016,7 @@ external void AnimationManager_resetToRestPose(
);
@ffi.Native<
ffi.Void Function(
ffi.Bool Function(
ffi.Pointer<TAnimationManager>,
ffi.Pointer<TSceneAsset>,
ffi.Int,
@@ -2995,7 +3027,7 @@ external void AnimationManager_resetToRestPose(
ffi.Float,
ffi.Float,
ffi.Float)>(isLeaf: true)
external void AnimationManager_addBoneAnimation(
external bool AnimationManager_addBoneAnimation(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> tSceneAsset,
int skinIndex,
@@ -3041,7 +3073,7 @@ external void AnimationManager_getInverseBindMatrix(
);
@ffi.Native<
ffi.Void Function(
ffi.Bool Function(
ffi.Pointer<TAnimationManager>,
ffi.Pointer<TSceneAsset>,
ffi.Int,
@@ -3050,9 +3082,9 @@ external void AnimationManager_getInverseBindMatrix(
ffi.Bool,
ffi.Float,
ffi.Float)>(isLeaf: true)
external void AnimationManager_playAnimation(
external bool AnimationManager_playGltfAnimation(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> sceneAsset,
ffi.Pointer<TSceneAsset> tSceneAsset,
int index,
bool loop,
bool reverse,
@@ -3062,9 +3094,9 @@ external void AnimationManager_playAnimation(
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
ffi.Bool Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
ffi.Int)>(isLeaf: true)
external void AnimationManager_stopAnimation(
external bool AnimationManager_stopGltfAnimation(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> sceneAsset,
int index,
@@ -3073,7 +3105,7 @@ external void AnimationManager_stopAnimation(
@ffi.Native<
ffi.Float Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
ffi.Int)>(isLeaf: true)
external double AnimationManager_getAnimationDuration(
external double AnimationManager_getGltfAnimationDuration(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> sceneAsset,
int animationIndex,
@@ -3082,7 +3114,7 @@ external double AnimationManager_getAnimationDuration(
@ffi.Native<
ffi.Int Function(
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
external int AnimationManager_getAnimationCount(
external int AnimationManager_getGltfAnimationCount(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> sceneAsset,
);
@@ -3090,7 +3122,7 @@ external int AnimationManager_getAnimationCount(
@ffi.Native<
ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
ffi.Pointer<ffi.Char>, ffi.Int)>(isLeaf: true)
external void AnimationManager_getAnimationName(
external void AnimationManager_getGltfAnimationName(
ffi.Pointer<TAnimationManager> tAnimationManager,
ffi.Pointer<TSceneAsset> sceneAsset,
ffi.Pointer<ffi.Char> outPtr,

View File

@@ -1,9 +1,6 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart' as v64;
import 'package:animation_tools_dart/src/bone_animation_data.dart';
import 'package:animation_tools_dart/src/morph_animation_data.dart';
import 'package:thermion_dart/src/filament/src/interface/layers.dart';
import 'package:thermion_dart/src/bindings/bindings.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_asset.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_scene.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_texture.dart';
@@ -159,7 +156,7 @@ class BackgroundImage extends ThermionAsset {
}
@override
Future addAnimationComponent(ThermionEntity entity) {
Future addAnimationComponent() {
// TODO: implement addAnimationComponent
throw UnimplementedError();
}
@@ -256,7 +253,7 @@ class BackgroundImage extends ThermionAsset {
}
@override
Future removeAnimationComponent(ThermionEntity entity) {
Future removeAnimationComponent() {
// TODO: implement removeAnimationComponent
throw UnimplementedError();
}
@@ -355,4 +352,6 @@ class BackgroundImage extends ThermionAsset {
Future<v64.Aabb3> getBoundingBox() {
throw UnimplementedError();
}
}

View File

@@ -493,16 +493,13 @@ class FFIAsset extends ThermionAsset {
if (weights.isEmpty) {
throw Exception("Weights must not be empty");
}
var weightsPtr = allocate<Float>(weights.length);
var weightsF32 = Float32List.fromList(weights);
for (int i = 0; i < weights.length; i++) {
weightsPtr[i] = weights[i];
}
var success = await withBoolCallback((cb) {
AnimationManager_setMorphTargetWeightsRenderThread(
animationManager, entity, weightsPtr, weights.length, cb);
animationManager, entity, weightsF32.address, weights.length, cb);
});
free(weightsPtr);
weightsF32.free();
if (!success) {
throw Exception(
@@ -521,6 +518,10 @@ class FFIAsset extends ThermionAsset {
var count = AnimationManager_getMorphTargetNameCount(
animationManager, asset, entity);
if (count < 0) {
throw Exception("Failed to retrieve morph target name count");
}
var outPtr = allocate<Char>(255);
for (int i = 0; i < count; i++) {
AnimationManager_getMorphTargetName(
@@ -559,13 +560,17 @@ class FFIAsset extends ThermionAsset {
///
///
@override
Future<List<String>> getAnimationNames() async {
Future<List<String>> getGltfAnimationNames() async {
var animationCount =
AnimationManager_getAnimationCount(animationManager, asset);
AnimationManager_getGltfAnimationCount(animationManager, asset);
if (animationCount == -1) {
throw Exception("This is not a glTF asset");
}
var names = <String>[];
var outPtr = allocate<Char>(255);
for (int i = 0; i < animationCount; i++) {
AnimationManager_getAnimationName(animationManager, asset, outPtr, i);
AnimationManager_getGltfAnimationName(animationManager, asset, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
free(outPtr);
@@ -577,8 +582,8 @@ class FFIAsset extends ThermionAsset {
///
///
@override
Future<double> getAnimationDuration(int animationIndex) async {
return AnimationManager_getAnimationDuration(
Future<double> getGltfAnimationDuration(int animationIndex) async {
return AnimationManager_getGltfAnimationDuration(
animationManager, asset, animationIndex);
}
@@ -586,12 +591,12 @@ class FFIAsset extends ThermionAsset {
///
///
Future<double> getAnimationDurationByName(String name) async {
var animations = await getAnimationNames();
var animations = await getGltfAnimationNames();
var index = animations.indexOf(name);
if (index == -1) {
throw Exception("Failed to find animation $name");
}
return getAnimationDuration(index);
return getGltfAnimationDuration(index);
}
///
@@ -904,47 +909,52 @@ class FFIAsset extends ThermionAsset {
///
///
@override
Future playAnimation(int index,
Future playGltfAnimation(int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0}) async {
AnimationManager_playAnimation(animationManager, asset, index, loop,
reverse, replaceActive, crossfade, startOffset);
if (!AnimationManager_playGltfAnimation(animationManager, asset, index,
loop, reverse, replaceActive, crossfade, startOffset)) {
throw Exception("Failed to play glTF animation. Check logs for details");
}
}
///
///
///
@override
Future stopAnimation(int animationIndex) async {
AnimationManager_stopAnimation(animationManager, asset, animationIndex);
Future stopGltfAnimation(int animationIndex) async {
if (!AnimationManager_stopGltfAnimation(
animationManager, asset, animationIndex)) {
throw Exception("Failed to stop glTF animation. Check logs for details");
}
}
///
///
///
@override
Future stopAnimationByName(String name) async {
var animations = await getAnimationNames();
await stopAnimation(animations.indexOf(name));
Future stopGltfAnimationByName(String name) async {
var animations = await getGltfAnimationNames();
await stopGltfAnimation(animations.indexOf(name));
}
///
///
///
@override
Future playAnimationByName(String name,
Future playGltfAnimationByName(String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
bool wait = false}) async {
var animations = await getAnimationNames();
var animations = await getGltfAnimationNames();
var index = animations.indexOf(name);
var duration = await getAnimationDuration(index);
await playAnimation(index,
var duration = await getGltfAnimationDuration(index);
await playGltfAnimation(index,
loop: loop,
reverse: reverse,
replaceActive: replaceActive,
@@ -967,14 +977,14 @@ class FFIAsset extends ThermionAsset {
///
///
@override
Future addAnimationComponent(ThermionEntity entity) async {
AnimationManager_addAnimationComponent(animationManager, entity);
Future addAnimationComponent() async {
AnimationManager_addGltfAnimationComponent(animationManager, this.asset);
}
///
///
///
Future removeAnimationComponent(ThermionEntity entity) async {
AnimationManager_removeAnimationComponent(animationManager, entity);
Future removeAnimationComponent() async {
AnimationManager_removeGltfAnimationComponent(animationManager, this.asset);
}
}

View File

@@ -66,8 +66,8 @@ abstract class ThermionAsset {
///
/// The dimensions of the bounding box for this asset.
/// This is independent of the boundingBoxAsset (which is used to visualize
/// the bounding box in the scene); you do not need to call
/// This is independent of the boundingBoxAsset (which is used to visualize
/// the bounding box in the scene); you do not need to call
/// [createBoundingBoxAsset] before this method.
Future<Aabb3> getBoundingBox();
@@ -153,36 +153,46 @@ abstract class ThermionAsset {
///
/// Schedules the glTF animation at [index] in [asset] to start playing on the next frame.
///
Future playAnimation(int index,
Future playGltfAnimation(int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0});
double startOffset = 0.0}) {
throw UnimplementedError();
}
///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
///
Future playAnimationByName(String name,
Future playGltfAnimationByName(String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0});
double crossfade = 0.0}) {
throw UnimplementedError();
}
///
///
///
Future setGltfAnimationFrame(int index, int animationFrame);
Future setGltfAnimationFrame(int index, int animationFrame) {
throw UnimplementedError();
}
///
///
///
Future stopAnimation(int animationIndex);
Future stopGltfAnimation(int animationIndex) {
throw UnimplementedError();
}
///
///
///
Future stopAnimationByName(String name);
Future stopGltfAnimationByName(String name) {
throw UnimplementedError();
}
///
/// Set the weights for all morph targets in [entity] to [weights].
@@ -191,27 +201,37 @@ abstract class ThermionAsset {
/// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name).
/// Use [getChildEntityByName] if you are setting the weights for a child mesh.
///
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights);
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights) {
throw UnimplementedError();
}
///
/// Gets the names of all morph targets for [entity] (which must be a renderable entity)
///
Future<List<String>> getMorphTargetNames({ThermionEntity? entity});
Future<List<String>> getMorphTargetNames({ThermionEntity? entity}) {
throw UnimplementedError();
}
///
/// Gets the names of all bones for the skin at [skinIndex].
///
Future<List<String>> getBoneNames({int skinIndex = 0});
Future<List<String>> getBoneNames({int skinIndex = 0}) {
throw UnimplementedError();
}
///
/// Gets the names of all glTF animations embedded in the specified entity.
///
Future<List<String>> getAnimationNames();
Future<List<String>> getGltfAnimationNames() {
throw UnimplementedError();
}
///
/// Returns the length (in seconds) of the animation at the given index.
///
Future<double> getAnimationDuration(int animationIndex);
Future<double> getGltfAnimationDuration(int animationIndex) {
throw UnimplementedError();
}
///
/// Construct animation(s) for every entity under [asset]. If [targetMeshNames] is provided, only entities with matching names will be animated.
@@ -221,12 +241,16 @@ abstract class ThermionAsset {
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
///
Future setMorphAnimationData(MorphAnimationData animation,
{List<String>? targetMeshNames});
{List<String>? targetMeshNames}) {
throw UnimplementedError();
}
///
/// Clear all current morph animations for [entity].
///
Future clearMorphAnimationData(ThermionEntity entity);
Future clearMorphAnimationData(ThermionEntity entity) {
throw UnimplementedError();
}
///
/// Resets all bones in the given entity to their rest pose.
@@ -307,10 +331,10 @@ abstract class ThermionAsset {
/// An [entity] will only be animatable after an animation component is attached.
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
///
Future addAnimationComponent(ThermionEntity entity);
Future addAnimationComponent();
///
/// Removes an animation component from [entity].
///
Future removeAnimationComponent(ThermionEntity entity);
Future removeAnimationComponent();
}