split animation components into GltfAnimation/MorphAnimation/BoneAnimation
This commit is contained in:
@@ -2935,20 +2935,52 @@ external void AnimationManager_update(
|
|||||||
int frameTimeInNanos,
|
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)>(
|
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
|
||||||
isLeaf: true)
|
isLeaf: true)
|
||||||
external void AnimationManager_addAnimationComponent(
|
external void AnimationManager_addMorphAnimationComponent(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
int entityId,
|
int entityId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
|
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
|
||||||
isLeaf: true)
|
isLeaf: true)
|
||||||
external void AnimationManager_removeAnimationComponent(
|
external void AnimationManager_removeMorphAnimationComponent(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
int entityId,
|
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.Native<
|
||||||
ffi.Bool Function(
|
ffi.Bool Function(
|
||||||
ffi.Pointer<TAnimationManager>,
|
ffi.Pointer<TAnimationManager>,
|
||||||
@@ -2984,7 +3016,7 @@ external void AnimationManager_resetToRestPose(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(
|
ffi.Bool Function(
|
||||||
ffi.Pointer<TAnimationManager>,
|
ffi.Pointer<TAnimationManager>,
|
||||||
ffi.Pointer<TSceneAsset>,
|
ffi.Pointer<TSceneAsset>,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
@@ -2995,7 +3027,7 @@ external void AnimationManager_resetToRestPose(
|
|||||||
ffi.Float,
|
ffi.Float,
|
||||||
ffi.Float,
|
ffi.Float,
|
||||||
ffi.Float)>(isLeaf: true)
|
ffi.Float)>(isLeaf: true)
|
||||||
external void AnimationManager_addBoneAnimation(
|
external bool AnimationManager_addBoneAnimation(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> tSceneAsset,
|
ffi.Pointer<TSceneAsset> tSceneAsset,
|
||||||
int skinIndex,
|
int skinIndex,
|
||||||
@@ -3041,7 +3073,7 @@ external void AnimationManager_getInverseBindMatrix(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(
|
ffi.Bool Function(
|
||||||
ffi.Pointer<TAnimationManager>,
|
ffi.Pointer<TAnimationManager>,
|
||||||
ffi.Pointer<TSceneAsset>,
|
ffi.Pointer<TSceneAsset>,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
@@ -3050,9 +3082,9 @@ external void AnimationManager_getInverseBindMatrix(
|
|||||||
ffi.Bool,
|
ffi.Bool,
|
||||||
ffi.Float,
|
ffi.Float,
|
||||||
ffi.Float)>(isLeaf: true)
|
ffi.Float)>(isLeaf: true)
|
||||||
external void AnimationManager_playAnimation(
|
external bool AnimationManager_playGltfAnimation(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> sceneAsset,
|
ffi.Pointer<TSceneAsset> tSceneAsset,
|
||||||
int index,
|
int index,
|
||||||
bool loop,
|
bool loop,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
@@ -3062,9 +3094,9 @@ external void AnimationManager_playAnimation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
ffi.Bool Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
||||||
ffi.Int)>(isLeaf: true)
|
ffi.Int)>(isLeaf: true)
|
||||||
external void AnimationManager_stopAnimation(
|
external bool AnimationManager_stopGltfAnimation(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> sceneAsset,
|
ffi.Pointer<TSceneAsset> sceneAsset,
|
||||||
int index,
|
int index,
|
||||||
@@ -3073,7 +3105,7 @@ external void AnimationManager_stopAnimation(
|
|||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Float Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
ffi.Float Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
||||||
ffi.Int)>(isLeaf: true)
|
ffi.Int)>(isLeaf: true)
|
||||||
external double AnimationManager_getAnimationDuration(
|
external double AnimationManager_getGltfAnimationDuration(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> sceneAsset,
|
ffi.Pointer<TSceneAsset> sceneAsset,
|
||||||
int animationIndex,
|
int animationIndex,
|
||||||
@@ -3082,7 +3114,7 @@ external double AnimationManager_getAnimationDuration(
|
|||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Int Function(
|
ffi.Int Function(
|
||||||
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
|
ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>)>(isLeaf: true)
|
||||||
external int AnimationManager_getAnimationCount(
|
external int AnimationManager_getGltfAnimationCount(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> sceneAsset,
|
ffi.Pointer<TSceneAsset> sceneAsset,
|
||||||
);
|
);
|
||||||
@@ -3090,7 +3122,7 @@ external int AnimationManager_getAnimationCount(
|
|||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Pointer<TSceneAsset>,
|
||||||
ffi.Pointer<ffi.Char>, ffi.Int)>(isLeaf: true)
|
ffi.Pointer<ffi.Char>, ffi.Int)>(isLeaf: true)
|
||||||
external void AnimationManager_getAnimationName(
|
external void AnimationManager_getGltfAnimationName(
|
||||||
ffi.Pointer<TAnimationManager> tAnimationManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
ffi.Pointer<TSceneAsset> sceneAsset,
|
ffi.Pointer<TSceneAsset> sceneAsset,
|
||||||
ffi.Pointer<ffi.Char> outPtr,
|
ffi.Pointer<ffi.Char> outPtr,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
import 'package:vector_math/vector_math_64.dart' as v64;
|
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/bone_animation_data.dart';
|
||||||
import 'package:animation_tools_dart/src/morph_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_asset.dart';
|
||||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_scene.dart';
|
import 'package:thermion_dart/src/filament/src/implementation/ffi_scene.dart';
|
||||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_texture.dart';
|
import 'package:thermion_dart/src/filament/src/implementation/ffi_texture.dart';
|
||||||
@@ -159,7 +156,7 @@ class BackgroundImage extends ThermionAsset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future addAnimationComponent(ThermionEntity entity) {
|
Future addAnimationComponent() {
|
||||||
// TODO: implement addAnimationComponent
|
// TODO: implement addAnimationComponent
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@@ -256,7 +253,7 @@ class BackgroundImage extends ThermionAsset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future removeAnimationComponent(ThermionEntity entity) {
|
Future removeAnimationComponent() {
|
||||||
// TODO: implement removeAnimationComponent
|
// TODO: implement removeAnimationComponent
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@@ -355,4 +352,6 @@ class BackgroundImage extends ThermionAsset {
|
|||||||
Future<v64.Aabb3> getBoundingBox() {
|
Future<v64.Aabb3> getBoundingBox() {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -493,16 +493,13 @@ class FFIAsset extends ThermionAsset {
|
|||||||
if (weights.isEmpty) {
|
if (weights.isEmpty) {
|
||||||
throw Exception("Weights must not be empty");
|
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) {
|
var success = await withBoolCallback((cb) {
|
||||||
AnimationManager_setMorphTargetWeightsRenderThread(
|
AnimationManager_setMorphTargetWeightsRenderThread(
|
||||||
animationManager, entity, weightsPtr, weights.length, cb);
|
animationManager, entity, weightsF32.address, weights.length, cb);
|
||||||
});
|
});
|
||||||
free(weightsPtr);
|
weightsF32.free();
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
@@ -521,6 +518,10 @@ class FFIAsset extends ThermionAsset {
|
|||||||
|
|
||||||
var count = AnimationManager_getMorphTargetNameCount(
|
var count = AnimationManager_getMorphTargetNameCount(
|
||||||
animationManager, asset, entity);
|
animationManager, asset, entity);
|
||||||
|
|
||||||
|
if (count < 0) {
|
||||||
|
throw Exception("Failed to retrieve morph target name count");
|
||||||
|
}
|
||||||
var outPtr = allocate<Char>(255);
|
var outPtr = allocate<Char>(255);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
AnimationManager_getMorphTargetName(
|
AnimationManager_getMorphTargetName(
|
||||||
@@ -559,13 +560,17 @@ class FFIAsset extends ThermionAsset {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<List<String>> getAnimationNames() async {
|
Future<List<String>> getGltfAnimationNames() async {
|
||||||
var animationCount =
|
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 names = <String>[];
|
||||||
var outPtr = allocate<Char>(255);
|
var outPtr = allocate<Char>(255);
|
||||||
for (int i = 0; i < animationCount; i++) {
|
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());
|
names.add(outPtr.cast<Utf8>().toDartString());
|
||||||
}
|
}
|
||||||
free(outPtr);
|
free(outPtr);
|
||||||
@@ -577,8 +582,8 @@ class FFIAsset extends ThermionAsset {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<double> getAnimationDuration(int animationIndex) async {
|
Future<double> getGltfAnimationDuration(int animationIndex) async {
|
||||||
return AnimationManager_getAnimationDuration(
|
return AnimationManager_getGltfAnimationDuration(
|
||||||
animationManager, asset, animationIndex);
|
animationManager, asset, animationIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,12 +591,12 @@ class FFIAsset extends ThermionAsset {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
Future<double> getAnimationDurationByName(String name) async {
|
Future<double> getAnimationDurationByName(String name) async {
|
||||||
var animations = await getAnimationNames();
|
var animations = await getGltfAnimationNames();
|
||||||
var index = animations.indexOf(name);
|
var index = animations.indexOf(name);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
throw Exception("Failed to find animation $name");
|
throw Exception("Failed to find animation $name");
|
||||||
}
|
}
|
||||||
return getAnimationDuration(index);
|
return getGltfAnimationDuration(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -904,47 +909,52 @@ class FFIAsset extends ThermionAsset {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future playAnimation(int index,
|
Future playGltfAnimation(int index,
|
||||||
{bool loop = false,
|
{bool loop = false,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
bool replaceActive = true,
|
bool replaceActive = true,
|
||||||
double crossfade = 0.0,
|
double crossfade = 0.0,
|
||||||
double startOffset = 0.0}) async {
|
double startOffset = 0.0}) async {
|
||||||
AnimationManager_playAnimation(animationManager, asset, index, loop,
|
if (!AnimationManager_playGltfAnimation(animationManager, asset, index,
|
||||||
reverse, replaceActive, crossfade, startOffset);
|
loop, reverse, replaceActive, crossfade, startOffset)) {
|
||||||
|
throw Exception("Failed to play glTF animation. Check logs for details");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future stopAnimation(int animationIndex) async {
|
Future stopGltfAnimation(int animationIndex) async {
|
||||||
AnimationManager_stopAnimation(animationManager, asset, animationIndex);
|
if (!AnimationManager_stopGltfAnimation(
|
||||||
|
animationManager, asset, animationIndex)) {
|
||||||
|
throw Exception("Failed to stop glTF animation. Check logs for details");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future stopAnimationByName(String name) async {
|
Future stopGltfAnimationByName(String name) async {
|
||||||
var animations = await getAnimationNames();
|
var animations = await getGltfAnimationNames();
|
||||||
await stopAnimation(animations.indexOf(name));
|
await stopGltfAnimation(animations.indexOf(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future playAnimationByName(String name,
|
Future playGltfAnimationByName(String name,
|
||||||
{bool loop = false,
|
{bool loop = false,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
bool replaceActive = true,
|
bool replaceActive = true,
|
||||||
double crossfade = 0.0,
|
double crossfade = 0.0,
|
||||||
bool wait = false}) async {
|
bool wait = false}) async {
|
||||||
var animations = await getAnimationNames();
|
var animations = await getGltfAnimationNames();
|
||||||
var index = animations.indexOf(name);
|
var index = animations.indexOf(name);
|
||||||
var duration = await getAnimationDuration(index);
|
var duration = await getGltfAnimationDuration(index);
|
||||||
await playAnimation(index,
|
await playGltfAnimation(index,
|
||||||
loop: loop,
|
loop: loop,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
replaceActive: replaceActive,
|
replaceActive: replaceActive,
|
||||||
@@ -967,14 +977,14 @@ class FFIAsset extends ThermionAsset {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future addAnimationComponent(ThermionEntity entity) async {
|
Future addAnimationComponent() async {
|
||||||
AnimationManager_addAnimationComponent(animationManager, entity);
|
AnimationManager_addGltfAnimationComponent(animationManager, this.asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
Future removeAnimationComponent(ThermionEntity entity) async {
|
Future removeAnimationComponent() async {
|
||||||
AnimationManager_removeAnimationComponent(animationManager, entity);
|
AnimationManager_removeGltfAnimationComponent(animationManager, this.asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ abstract class ThermionAsset {
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// The dimensions of the bounding box for this asset.
|
/// The dimensions of the bounding box for this asset.
|
||||||
/// This is independent of the boundingBoxAsset (which is used to visualize
|
/// This is independent of the boundingBoxAsset (which is used to visualize
|
||||||
/// the bounding box in the scene); you do not need to call
|
/// the bounding box in the scene); you do not need to call
|
||||||
/// [createBoundingBoxAsset] before this method.
|
/// [createBoundingBoxAsset] before this method.
|
||||||
Future<Aabb3> getBoundingBox();
|
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.
|
/// 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 loop = false,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
bool replaceActive = true,
|
bool replaceActive = true,
|
||||||
double crossfade = 0.0,
|
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.
|
/// 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 loop = false,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
bool replaceActive = true,
|
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].
|
/// 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).
|
/// 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.
|
/// 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)
|
/// 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].
|
/// 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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
|
||||||
///
|
///
|
||||||
Future setMorphAnimationData(MorphAnimationData animation,
|
Future setMorphAnimationData(MorphAnimationData animation,
|
||||||
{List<String>? targetMeshNames});
|
{List<String>? targetMeshNames}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Clear all current morph animations for [entity].
|
/// 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.
|
/// 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.
|
/// 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.
|
/// 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].
|
/// Removes an animation component from [entity].
|
||||||
///
|
///
|
||||||
Future removeAnimationComponent(ThermionEntity entity);
|
Future removeAnimationComponent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,14 +32,7 @@ namespace thermion
|
|||||||
|
|
||||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
|
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
|
||||||
|
|
||||||
enum AnimationType
|
struct Animation
|
||||||
{
|
|
||||||
MORPH,
|
|
||||||
BONE,
|
|
||||||
GLTF
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimationStatus
|
|
||||||
{
|
{
|
||||||
time_point_t start = time_point_t::max();
|
time_point_t start = time_point_t::max();
|
||||||
float startOffset;
|
float startOffset;
|
||||||
@@ -52,7 +45,7 @@ namespace thermion
|
|||||||
/// The status of an animation embedded in a glTF object.
|
/// The status of an animation embedded in a glTF object.
|
||||||
/// @param index refers to the index of the animation in the animations property of the underlying object.
|
/// @param index refers to the index of the animation in the animations property of the underlying object.
|
||||||
///
|
///
|
||||||
struct GltfAnimation : AnimationStatus
|
struct GltfAnimation : Animation
|
||||||
{
|
{
|
||||||
int index = -1;
|
int index = -1;
|
||||||
};
|
};
|
||||||
@@ -60,24 +53,18 @@ namespace thermion
|
|||||||
//
|
//
|
||||||
// The status of a morph target animation created dynamically at runtime (not glTF embedded).
|
// The status of a morph target animation created dynamically at runtime (not glTF embedded).
|
||||||
//
|
//
|
||||||
struct MorphAnimation : AnimationStatus
|
struct MorphAnimation : Animation
|
||||||
{
|
{
|
||||||
utils::Entity meshTarget;
|
int lengthInFrames;
|
||||||
int numFrames = -1;
|
|
||||||
float frameLengthInMs = 0;
|
float frameLengthInMs = 0;
|
||||||
std::vector<float> frameData;
|
std::vector<float> frameData;
|
||||||
std::vector<int> morphIndices;
|
std::vector<int> morphIndices;
|
||||||
int lengthInFrames;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
struct BoneAnimation : Animation {
|
||||||
// The status of a skeletal animation created dynamically at runtime (not glTF embedded).
|
int lengthInFrames;
|
||||||
//
|
|
||||||
struct BoneAnimation : AnimationStatus
|
|
||||||
{
|
|
||||||
size_t boneIndex;
|
size_t boneIndex;
|
||||||
size_t skinIndex = 0;
|
size_t skinIndex = 0;
|
||||||
int lengthInFrames;
|
|
||||||
float frameLengthInMs = 0;
|
float frameLengthInMs = 0;
|
||||||
std::vector<math::mat4f> frameData;
|
std::vector<math::mat4f> frameData;
|
||||||
float fadeOutInSecs = 0;
|
float fadeOutInSecs = 0;
|
||||||
@@ -85,276 +72,86 @@ namespace thermion
|
|||||||
float maxDelta = 1.0f;
|
float maxDelta = 1.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimationComponent
|
/// @brief
|
||||||
|
///
|
||||||
|
///
|
||||||
|
struct BoneAnimationComponent
|
||||||
{
|
{
|
||||||
std::variant<FilamentInstance *, Entity> target;
|
FilamentInstance * target;
|
||||||
std::vector<GltfAnimation> gltfAnimations;
|
std::vector<BoneAnimation> animations;
|
||||||
std::vector<MorphAnimation> morphAnimations;
|
};
|
||||||
std::vector<BoneAnimation> boneAnimations;
|
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
///
|
||||||
|
///
|
||||||
|
struct MorphAnimationComponent
|
||||||
|
{
|
||||||
|
std::vector<MorphAnimation> animations;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
///
|
||||||
|
///
|
||||||
|
struct GltfAnimationComponent
|
||||||
|
{
|
||||||
|
FilamentInstance * target;
|
||||||
// the index of the last active glTF animation,
|
// the index of the last active glTF animation,
|
||||||
// used to cross-fade
|
// used to cross-fade
|
||||||
int fadeGltfAnimationIndex = -1;
|
int fadeGltfAnimationIndex = -1;
|
||||||
float fadeDuration = 0.0f;
|
float fadeDuration = 0.0f;
|
||||||
float fadeOutAnimationStart = 0.0f;
|
float fadeOutAnimationStart = 0.0f;
|
||||||
|
std::vector<GltfAnimation> animations;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimationComponentManager : public utils::SingleInstanceComponentManager<AnimationComponent>
|
|
||||||
{
|
|
||||||
|
|
||||||
filament::TransformManager &_transformManager;
|
class GltfAnimationComponentManager : public utils::SingleInstanceComponentManager<GltfAnimationComponent> {
|
||||||
filament::RenderableManager &_renderableManager;
|
public:
|
||||||
|
GltfAnimationComponentManager(
|
||||||
|
filament::TransformManager &transformManager,
|
||||||
|
filament::RenderableManager &renderableManager) :
|
||||||
|
mTransformManager(transformManager), mRenderableManager(renderableManager) {};
|
||||||
|
~GltfAnimationComponentManager() = default;
|
||||||
|
void addAnimationComponent(FilamentInstance *target);
|
||||||
|
void removeAnimationComponent(FilamentInstance *target);
|
||||||
|
void update();
|
||||||
|
|
||||||
public:
|
private:
|
||||||
AnimationComponentManager(
|
filament::TransformManager &mTransformManager;
|
||||||
filament::TransformManager &transformManager,
|
filament::RenderableManager &mRenderableManager;
|
||||||
filament::RenderableManager &renderableManager) : _transformManager(transformManager),
|
|
||||||
_renderableManager(renderableManager){};
|
|
||||||
|
|
||||||
void addAnimationComponent(std::variant<FilamentInstance *, Entity> target)
|
|
||||||
{
|
|
||||||
|
|
||||||
AnimationComponent animationComponent;
|
|
||||||
animationComponent.target = target;
|
|
||||||
EntityInstanceBase::Type componentInstance;
|
|
||||||
if (std::holds_alternative<FilamentInstance *>(target))
|
|
||||||
{
|
|
||||||
auto instance = std::get<FilamentInstance *>(target);
|
|
||||||
if(!hasComponent(instance->getRoot())) {
|
|
||||||
componentInstance = addComponent(instance->getRoot());
|
|
||||||
this->elementAt<0>(componentInstance) = animationComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto entity = std::get<Entity>(target);
|
|
||||||
if(!hasComponent(entity)) {
|
|
||||||
componentInstance = addComponent(entity);
|
|
||||||
this->elementAt<0>(componentInstance) = animationComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void removeAnimationComponent(std::variant<FilamentInstance *, Entity> target)
|
|
||||||
{
|
|
||||||
AnimationComponent animationComponent;
|
|
||||||
animationComponent.target = target;
|
|
||||||
EntityInstanceBase::Type componentInstance;
|
|
||||||
if (std::holds_alternative<FilamentInstance *>(target))
|
|
||||||
{
|
|
||||||
auto instance = std::get<FilamentInstance *>(target);
|
|
||||||
if(hasComponent(instance->getRoot())) {
|
|
||||||
removeComponent(instance->getRoot());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto entity = std::get<Entity>(target);
|
|
||||||
if(hasComponent(entity)) {
|
|
||||||
removeComponent(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update()
|
|
||||||
{
|
|
||||||
|
|
||||||
for (auto it = begin(); it < end(); it++)
|
|
||||||
{
|
|
||||||
const auto &entity = getEntity(it);
|
|
||||||
|
|
||||||
auto componentInstance = getInstance(entity);
|
|
||||||
auto &animationComponent = elementAt<0>(componentInstance);
|
|
||||||
|
|
||||||
auto &morphAnimations = animationComponent.morphAnimations;
|
|
||||||
|
|
||||||
if (std::holds_alternative<FilamentInstance *>(animationComponent.target))
|
|
||||||
{
|
|
||||||
auto target = std::get<FilamentInstance *>(animationComponent.target);
|
|
||||||
auto animator = target->getAnimator();
|
|
||||||
auto &gltfAnimations = animationComponent.gltfAnimations;
|
|
||||||
auto &boneAnimations = animationComponent.boneAnimations;
|
|
||||||
|
|
||||||
if(gltfAnimations.size() > 0) {
|
|
||||||
for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
auto now = high_resolution_clock::now();
|
|
||||||
|
|
||||||
auto animationStatus = animationComponent.gltfAnimations[i];
|
|
||||||
|
|
||||||
auto elapsedInSecs = animationStatus.startOffset + float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
|
|
||||||
|
|
||||||
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
|
|
||||||
{
|
|
||||||
animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001);
|
|
||||||
animator->updateBoneMatrices();
|
|
||||||
gltfAnimations.erase(gltfAnimations.begin() + i);
|
|
||||||
animationComponent.fadeGltfAnimationIndex = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
animator->applyAnimation(animationStatus.index, elapsedInSecs);
|
|
||||||
|
|
||||||
if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration)
|
|
||||||
{
|
|
||||||
// cross-fade
|
|
||||||
auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs;
|
|
||||||
auto alpha = elapsedInSecs / animationComponent.fadeDuration;
|
|
||||||
animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
animator->updateBoneMatrices();
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// When fading in/out, interpolate between the "current" transform (which has possibly been set by the glTF animation loop above)
|
|
||||||
/// and the first (for fading in) or last (for fading out) frame.
|
|
||||||
///
|
|
||||||
for (int i = (int)boneAnimations.size() - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
auto animationStatus = boneAnimations[i];
|
|
||||||
|
|
||||||
auto now = high_resolution_clock::now();
|
|
||||||
|
|
||||||
auto elapsedInMillis = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count());
|
|
||||||
auto elapsedInSecs = elapsedInMillis / 1000.0f;
|
|
||||||
|
|
||||||
// if we're not looping and the amount of time elapsed is greater than the animation duration plus the fade-in/out buffer,
|
|
||||||
// then the animation is completed and we can delete it
|
|
||||||
if (elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
|
||||||
{
|
|
||||||
if(!animationStatus.loop) {
|
|
||||||
boneAnimations.erase(boneAnimations.begin() + i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're fading in, treat elapsedFrames is zero (and fading out, treat elapsedFrames as lengthInFrames)
|
|
||||||
float elapsedInFrames = (elapsedInMillis - (1000 * animationStatus.fadeInInSecs)) / animationStatus.frameLengthInMs;
|
|
||||||
int currFrame = std::floor(elapsedInFrames);
|
|
||||||
int nextFrame = currFrame;
|
|
||||||
|
|
||||||
// offset from the end if reverse
|
|
||||||
if (animationStatus.reverse)
|
|
||||||
{
|
|
||||||
currFrame = animationStatus.lengthInFrames - currFrame;
|
|
||||||
if (currFrame > 0)
|
|
||||||
{
|
|
||||||
nextFrame = currFrame - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nextFrame = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (currFrame < animationStatus.lengthInFrames - 1)
|
|
||||||
{
|
|
||||||
nextFrame = currFrame + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nextFrame = currFrame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currFrame = std::clamp(currFrame, 0, animationStatus.lengthInFrames - 1);
|
|
||||||
nextFrame = std::clamp(nextFrame, 0, animationStatus.lengthInFrames - 1);
|
|
||||||
|
|
||||||
float frameDelta = elapsedInFrames - currFrame;
|
|
||||||
|
|
||||||
// linearly interpolate this animation between its last/current frames
|
|
||||||
// this is to avoid jerky animations when the animation framerate is slower than our tick rate
|
|
||||||
|
|
||||||
math::float3 currScale, newScale;
|
|
||||||
math::quatf currRotation, newRotation;
|
|
||||||
math::float3 currTranslation, newTranslation;
|
|
||||||
math::mat4f curr = animationStatus.frameData[currFrame];
|
|
||||||
decomposeMatrix(curr, &currTranslation, &currRotation, &currScale);
|
|
||||||
|
|
||||||
if(frameDelta > 0) {
|
|
||||||
math::mat4f next = animationStatus.frameData[nextFrame];
|
|
||||||
decomposeMatrix(next, &newTranslation, &newRotation, &newScale);
|
|
||||||
newScale = mix(currScale, newScale, frameDelta);
|
|
||||||
newRotation = slerp(currRotation, newRotation, frameDelta);
|
|
||||||
newTranslation = mix(currTranslation, newTranslation, frameDelta);
|
|
||||||
} else {
|
|
||||||
newScale = currScale;
|
|
||||||
newRotation = currRotation;
|
|
||||||
newTranslation = currTranslation;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Entity joint = target->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
|
|
||||||
|
|
||||||
// now calculate the fade out/in delta
|
|
||||||
// if we're fading in, this will be 0.0 at the start of the fade and 1.0 at the end
|
|
||||||
auto fadeDelta = elapsedInSecs / animationStatus.fadeInInSecs;
|
|
||||||
|
|
||||||
// // if we're fading out, this will be 1.0 at the start of the fade and 0.0 at the end
|
|
||||||
if(fadeDelta > 1.0f) {
|
|
||||||
fadeDelta = 1 - ((elapsedInSecs - animationStatus.durationInSecs - animationStatus.fadeInInSecs) / animationStatus.fadeOutInSecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
fadeDelta = std::clamp(fadeDelta, 0.0f, animationStatus.maxDelta);
|
|
||||||
|
|
||||||
auto jointTransform = _transformManager.getInstance(joint);
|
|
||||||
|
|
||||||
// linearly interpolate this animation between its current (interpolated) frame and the current transform (i.e. as set by the gltf frame)
|
|
||||||
// // if we are fading in or out, apply a delta
|
|
||||||
if (fadeDelta >= 0.0f && fadeDelta <= 1.0f) {
|
|
||||||
math::float3 fadeScale;
|
|
||||||
math::quatf fadeRotation;
|
|
||||||
math::float3 fadeTranslation;
|
|
||||||
auto currentTransform = _transformManager.getTransform(jointTransform);
|
|
||||||
decomposeMatrix(currentTransform, &fadeTranslation, &fadeRotation, &fadeScale);
|
|
||||||
newScale = mix(fadeScale, newScale, fadeDelta);
|
|
||||||
newRotation = slerp(fadeRotation, newRotation, fadeDelta);
|
|
||||||
newTranslation = mix(fadeTranslation, newTranslation, fadeDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
_transformManager.setTransform(jointTransform, composeMatrix(newTranslation, newRotation, newScale));
|
|
||||||
|
|
||||||
animator->updateBoneMatrices();
|
|
||||||
|
|
||||||
if (animationStatus.loop && elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
|
||||||
{
|
|
||||||
animationStatus.start = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = (int)morphAnimations.size() - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
|
|
||||||
auto now = high_resolution_clock::now();
|
|
||||||
|
|
||||||
auto animationStatus = morphAnimations[i];
|
|
||||||
|
|
||||||
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
|
|
||||||
|
|
||||||
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
|
|
||||||
{
|
|
||||||
morphAnimations.erase(morphAnimations.begin() + i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames;
|
|
||||||
// offset from the end if reverse
|
|
||||||
if (animationStatus.reverse)
|
|
||||||
{
|
|
||||||
frameNumber = animationStatus.lengthInFrames - frameNumber;
|
|
||||||
}
|
|
||||||
auto baseOffset = frameNumber * animationStatus.morphIndices.size();
|
|
||||||
for (int i = 0; i < animationStatus.morphIndices.size(); i++)
|
|
||||||
{
|
|
||||||
auto morphIndex = animationStatus.morphIndices[i];
|
|
||||||
// set the weights appropriately
|
|
||||||
_renderableManager.setMorphWeights(
|
|
||||||
_renderableManager.getInstance(animationStatus.meshTarget),
|
|
||||||
animationStatus.frameData.data() + baseOffset + i,
|
|
||||||
1,
|
|
||||||
morphIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BoneAnimationComponentManager : public utils::SingleInstanceComponentManager<BoneAnimationComponent> {
|
||||||
|
public:
|
||||||
|
BoneAnimationComponentManager(
|
||||||
|
filament::TransformManager &transformManager,
|
||||||
|
filament::RenderableManager &renderableManager) :
|
||||||
|
mTransformManager(transformManager), mRenderableManager(renderableManager) {};
|
||||||
|
~BoneAnimationComponentManager() {};
|
||||||
|
|
||||||
|
void addAnimationComponent(FilamentInstance *target);
|
||||||
|
void removeAnimationComponent(FilamentInstance *target);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
filament::TransformManager &mTransformManager;
|
||||||
|
filament::RenderableManager &mRenderableManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorphAnimationComponentManager : public utils::SingleInstanceComponentManager<MorphAnimationComponent> {
|
||||||
|
public:
|
||||||
|
MorphAnimationComponentManager(
|
||||||
|
filament::TransformManager &transformManager,
|
||||||
|
filament::RenderableManager &renderableManager) :
|
||||||
|
mTransformManager(transformManager), mRenderableManager(renderableManager) {};
|
||||||
|
~MorphAnimationComponentManager() {};
|
||||||
|
|
||||||
|
void addAnimationComponent(Entity entity);
|
||||||
|
void removeAnimationComponent(Entity entity);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
filament::TransformManager &mTransformManager;
|
||||||
|
filament::RenderableManager &mRenderableManager;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace thermion
|
|||||||
AnimationManager(
|
AnimationManager(
|
||||||
Engine *engine,
|
Engine *engine,
|
||||||
Scene *scene);
|
Scene *scene);
|
||||||
~AnimationManager();
|
~AnimationManager() = default;
|
||||||
|
|
||||||
/// @brief
|
/// @brief
|
||||||
///
|
///
|
||||||
@@ -158,18 +158,39 @@ namespace thermion
|
|||||||
float getGltfAnimationDuration(GltfSceneAssetInstance *instance, int animationIndex);
|
float getGltfAnimationDuration(GltfSceneAssetInstance *instance, int animationIndex);
|
||||||
|
|
||||||
/// @brief
|
/// @brief
|
||||||
/// @param entity
|
/// @param instance
|
||||||
/// @return
|
/// @return
|
||||||
bool addAnimationComponent(EntityId entity);
|
bool addGltfAnimationComponent(GltfSceneAssetInstance *instance);
|
||||||
|
|
||||||
/// @brief
|
/// @brief
|
||||||
/// @param entity
|
/// @param instance
|
||||||
void removeAnimationComponent(EntityId entity);
|
void removeGltfAnimationComponent(GltfSceneAssetInstance *instance);
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
/// @param instance
|
||||||
|
/// @return
|
||||||
|
bool addBoneAnimationComponent(GltfSceneAssetInstance *instance);
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
/// @param instance
|
||||||
|
void removeBoneAnimationComponent(GltfSceneAssetInstance *instance);
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
/// @param asset
|
||||||
|
/// @return
|
||||||
|
bool addMorphAnimationComponent(utils::Entity entity);
|
||||||
|
|
||||||
|
/// @brief
|
||||||
|
/// @param asset
|
||||||
|
void removeMorphAnimationComponent(utils::Entity entity);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Engine *_engine = nullptr;
|
Engine *_engine = nullptr;
|
||||||
Scene *_scene = nullptr;
|
Scene *_scene = nullptr;
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
std::unique_ptr<AnimationComponentManager> _animationComponentManager = std::nullptr_t();
|
std::unique_ptr<GltfAnimationComponentManager> _gltfAnimationComponentManager = std::nullptr_t();
|
||||||
|
std::unique_ptr<MorphAnimationComponentManager> _morphAnimationComponentManager = std::nullptr_t();
|
||||||
|
std::unique_ptr<BoneAnimationComponentManager> _boneAnimationComponentManager = std::nullptr_t();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
#include <utils/Entity.h>
|
||||||
|
|
||||||
#include "c_api/APIExport.h"
|
#include "c_api/APIExport.h"
|
||||||
#include "scene/AnimationManager.hpp"
|
#include "scene/AnimationManager.hpp"
|
||||||
|
|
||||||
@@ -26,15 +28,65 @@ extern "C"
|
|||||||
animationManager->update(frameTimeInNanos);
|
animationManager->update(frameTimeInNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_addAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId)
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_addGltfAnimationComponent(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset)
|
||||||
{
|
{
|
||||||
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf || !sceneAsset->isInstance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
animationManager->addAnimationComponent(entityId);
|
|
||||||
|
animationManager->addGltfAnimationComponent(reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_removeAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId)
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_removeGltfAnimationComponent(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset)
|
||||||
|
{
|
||||||
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf || !sceneAsset->isInstance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
animationManager->removeGltfAnimationComponent(reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_addBoneAnimationComponent(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset)
|
||||||
|
{
|
||||||
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf || !sceneAsset->isInstance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
|
||||||
|
animationManager->addBoneAnimationComponent(reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_removeBoneAnimationComponent(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset)
|
||||||
|
{
|
||||||
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf || !sceneAsset->isInstance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
animationManager->removeBoneAnimationComponent(reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_addMorphAnimationComponent(TAnimationManager *tAnimationManager, EntityId entity)
|
||||||
{
|
{
|
||||||
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
animationManager->removeAnimationComponent(entityId);
|
animationManager->addMorphAnimationComponent(utils::Entity::import(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_removeMorphAnimationComponent(TAnimationManager *tAnimationManager, EntityId entity)
|
||||||
|
{
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
animationManager->removeMorphAnimationComponent(utils::Entity::import(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE bool AnimationManager_setMorphAnimation(
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_setMorphAnimation(
|
||||||
@@ -66,26 +118,26 @@ extern "C"
|
|||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE bool AnimationManager_clearMorphAnimation(TAnimationManager *tAnimationManager, EntityId entityId)
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_clearMorphAnimation(TAnimationManager *tAnimationManager, EntityId entityId)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto entity = utils::Entity::import(entityId);
|
auto entity = utils::Entity::import(entityId);
|
||||||
animManager->clearMorphAnimationBuffer(entity);
|
animationManager->clearMorphAnimationBuffer(entity);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_resetToRestPose(TAnimationManager *tAnimationManager, TSceneAsset *sceneAsset)
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_resetToRestPose(TAnimationManager *tAnimationManager, TSceneAsset *sceneAsset)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
||||||
{
|
{
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
||||||
animManager->resetToRestPose(instance);
|
animationManager->resetToRestPose(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_addBoneAnimation(
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_addBoneAnimation(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
int skinIndex,
|
int skinIndex,
|
||||||
int boneIndex,
|
int boneIndex,
|
||||||
const float *const frameData,
|
const float *const frameData,
|
||||||
@@ -95,13 +147,24 @@ extern "C"
|
|||||||
float fadeInInSecs,
|
float fadeInInSecs,
|
||||||
float maxDelta)
|
float maxDelta)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
return false;
|
||||||
{
|
|
||||||
animManager->addBoneAnimation(reinterpret_cast<GltfSceneAssetInstance *>(asset), skinIndex, boneIndex, frameData, numFrames, frameLengthInMs,
|
|
||||||
fadeOutInSecs, fadeInInSecs, maxDelta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
|
||||||
|
if (sceneAsset->isInstance())
|
||||||
|
{
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
animationManager->addBoneAnimationComponent(instance);
|
||||||
|
animationManager->addBoneAnimation(instance, skinIndex, boneIndex, frameData, numFrames, frameLengthInMs, fadeOutInSecs, fadeInInSecs, maxDelta);
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId AnimationManager_getBone(
|
EMSCRIPTEN_KEEPALIVE EntityId AnimationManager_getBone(
|
||||||
@@ -110,11 +173,11 @@ extern "C"
|
|||||||
int skinIndex,
|
int skinIndex,
|
||||||
int boneIndex)
|
int boneIndex)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
||||||
{
|
{
|
||||||
auto entities = animManager->getBoneEntities(reinterpret_cast<GltfSceneAssetInstance *>(asset), skinIndex);
|
auto entities = animationManager->getBoneEntities(reinterpret_cast<GltfSceneAssetInstance *>(asset), skinIndex);
|
||||||
if (boneIndex < entities.size())
|
if (boneIndex < entities.size())
|
||||||
{
|
{
|
||||||
return utils::Entity::smuggle(entities[boneIndex]);
|
return utils::Entity::smuggle(entities[boneIndex]);
|
||||||
@@ -131,12 +194,12 @@ extern "C"
|
|||||||
float *const out,
|
float *const out,
|
||||||
int numBones)
|
int numBones)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
||||||
{
|
{
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
||||||
const auto transforms = animManager->getBoneRestTranforms(instance, skinIndex);
|
const auto transforms = animationManager->getBoneRestTranforms(instance, skinIndex);
|
||||||
auto numTransforms = transforms.size();
|
auto numTransforms = transforms.size();
|
||||||
if (numTransforms != numBones)
|
if (numTransforms != numBones)
|
||||||
{
|
{
|
||||||
@@ -164,12 +227,12 @@ extern "C"
|
|||||||
int boneIndex,
|
int boneIndex,
|
||||||
float *const out)
|
float *const out)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
||||||
{
|
{
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
||||||
auto transform = animManager->getInverseBindMatrix(instance, skinIndex, boneIndex);
|
auto transform = animationManager->getInverseBindMatrix(instance, skinIndex, boneIndex);
|
||||||
for (int colNum = 0; colNum < 4; colNum++)
|
for (int colNum = 0; colNum < 4; colNum++)
|
||||||
{
|
{
|
||||||
for (int rowNum = 0; rowNum < 4; rowNum++)
|
for (int rowNum = 0; rowNum < 4; rowNum++)
|
||||||
@@ -180,9 +243,9 @@ extern "C"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_playAnimation(
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_playGltfAnimation(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
int index,
|
int index,
|
||||||
bool loop,
|
bool loop,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
@@ -190,29 +253,48 @@ extern "C"
|
|||||||
float crossfade,
|
float crossfade,
|
||||||
float startOffset)
|
float startOffset)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
{
|
return false;
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
|
||||||
|
|
||||||
animManager->playGltfAnimation(instance, index, loop, reverse, replaceActive, crossfade, startOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
|
||||||
|
if (sceneAsset->isInstance())
|
||||||
|
{
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
animationManager->addGltfAnimationComponent(instance);
|
||||||
|
animationManager->playGltfAnimation(instance, index, loop, reverse, replaceActive, crossfade, startOffset);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_stopAnimation(
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_stopGltfAnimation(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(sceneAsset);
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
return false;
|
||||||
{
|
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
|
||||||
|
|
||||||
animManager->stopGltfAnimation(instance, index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
|
||||||
|
if (sceneAsset->isInstance())
|
||||||
|
{
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
animationManager->stopGltfAnimation(instance, index);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_setGltfAnimationFrame(
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_setGltfAnimationFrame(
|
||||||
@@ -221,41 +303,79 @@ extern "C"
|
|||||||
int animationIndex,
|
int animationIndex,
|
||||||
int frame)
|
int frame)
|
||||||
{
|
{
|
||||||
auto *animManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
auto *animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
auto asset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
auto asset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
if (asset->getType() == SceneAsset::SceneAssetType::Gltf && asset->isInstance())
|
||||||
{
|
{
|
||||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset);
|
||||||
animManager->setGltfAnimationFrame(instance, animationIndex, frame);
|
animationManager->setGltfAnimationFrame(instance, animationIndex, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE float AnimationManager_getAnimationDuration(
|
EMSCRIPTEN_KEEPALIVE float AnimationManager_getGltfAnimationDuration(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
int animationIndex)
|
int animationIndex)
|
||||||
{
|
{
|
||||||
auto instance = ((GltfSceneAssetInstance *)sceneAsset);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
return ((AnimationManager *)tAnimationManager)->getGltfAnimationDuration(instance, animationIndex);
|
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
|
||||||
|
if (sceneAsset->isInstance())
|
||||||
|
{
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return animationManager->getGltfAnimationDuration(instance, animationIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE int AnimationManager_getAnimationCount(
|
EMSCRIPTEN_KEEPALIVE int AnimationManager_getGltfAnimationCount(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset)
|
TSceneAsset *tSceneAsset)
|
||||||
{
|
{
|
||||||
auto instance = ((GltfSceneAssetInstance *)sceneAsset);
|
|
||||||
auto names = ((AnimationManager *)tAnimationManager)->getGltfAnimationNames(instance);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
if(sceneAsset->isInstance()) {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
auto names = animationManager->getGltfAnimationNames(instance);
|
||||||
|
TRACE("Animation count : %d", names.size());
|
||||||
return (int)names.size();
|
return (int)names.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_getAnimationName(
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_getGltfAnimationName(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
char *const outPtr,
|
char *const outPtr,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
auto instance = ((GltfSceneAssetInstance *)sceneAsset);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
auto names = ((AnimationManager *)tAnimationManager)->getGltfAnimationNames(instance);
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
|
strcpy(outPtr, "FILAMENT_ERROR_NOT_FOUND");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAssetInstance *instance;
|
||||||
|
if(sceneAsset->isInstance()) {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
} else {
|
||||||
|
instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset->getInstanceAt(0));
|
||||||
|
}
|
||||||
|
auto names = animationManager->getGltfAnimationNames(instance);
|
||||||
std::string name = names[index];
|
std::string name = names[index];
|
||||||
strcpy(outPtr, name.c_str());
|
strcpy(outPtr, name.c_str());
|
||||||
}
|
}
|
||||||
@@ -278,8 +398,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
auto instance = ((GltfSceneAssetInstance *)sceneAsset);
|
auto instance = ((GltfSceneAssetInstance *)sceneAsset);
|
||||||
auto entities = ((AnimationManager *)tAnimationManager)->getBoneEntities(instance, skinIndex);
|
auto entities = ((AnimationManager *)tAnimationManager)->getBoneEntities(instance, skinIndex);
|
||||||
// Note: This needs implementation of a method to get bone names from entities
|
|
||||||
// Current source doesn't show how bone names are retrieved
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE bool AnimationManager_updateBoneMatrices(
|
EMSCRIPTEN_KEEPALIVE bool AnimationManager_updateBoneMatrices(
|
||||||
@@ -293,11 +412,27 @@ extern "C"
|
|||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE int AnimationManager_getMorphTargetNameCount(
|
EMSCRIPTEN_KEEPALIVE int AnimationManager_getMorphTargetNameCount(
|
||||||
TAnimationManager *tAnimationManager,
|
TAnimationManager *tAnimationManager,
|
||||||
TSceneAsset *sceneAsset,
|
TSceneAsset *tSceneAsset,
|
||||||
EntityId childEntity)
|
EntityId childEntity)
|
||||||
{
|
{
|
||||||
auto asset = ((GltfSceneAsset *)sceneAsset);
|
auto sceneAsset = reinterpret_cast<SceneAsset *>(tSceneAsset);
|
||||||
auto names = ((AnimationManager *)tAnimationManager)->getMorphTargetNames(asset, childEntity);
|
|
||||||
|
if(sceneAsset->getType() != SceneAsset::SceneAssetType::Gltf) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
|
||||||
|
GltfSceneAsset *gltfAsset;
|
||||||
|
|
||||||
|
if (sceneAsset->isInstance())
|
||||||
|
{
|
||||||
|
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(sceneAsset);
|
||||||
|
gltfAsset = reinterpret_cast<GltfSceneAsset *>(instance->getInstanceOwner());
|
||||||
|
} else {
|
||||||
|
gltfAsset = reinterpret_cast<GltfSceneAsset *>(sceneAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto names = animationManager->getMorphTargetNames(gltfAsset, childEntity);
|
||||||
return (int)names.size();
|
return (int)names.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include "components/AnimationComponentManager.hpp"
|
||||||
|
|
||||||
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
namespace thermion
|
||||||
|
{
|
||||||
|
|
||||||
|
void BoneAnimationComponentManager::addAnimationComponent(FilamentInstance *target) {
|
||||||
|
if(!hasComponent(target->getRoot())) {
|
||||||
|
EntityInstanceBase::Type componentInstance = addComponent(target->getRoot());
|
||||||
|
this->elementAt<0>(componentInstance) = { target };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneAnimationComponentManager::removeAnimationComponent(FilamentInstance *target) {
|
||||||
|
if(hasComponent(target->getRoot())) {
|
||||||
|
removeComponent(target->getRoot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneAnimationComponentManager::update() {
|
||||||
|
TRACE("Updating with %d components", getComponentCount());
|
||||||
|
for (auto it = begin(); it < end(); it++)
|
||||||
|
{
|
||||||
|
const auto &entity = getEntity(it);
|
||||||
|
|
||||||
|
auto componentInstance = getInstance(entity);
|
||||||
|
auto &animationComponent = elementAt<0>(componentInstance);
|
||||||
|
|
||||||
|
auto &boneAnimations = animationComponent.animations;
|
||||||
|
|
||||||
|
auto target = animationComponent.target;
|
||||||
|
auto animator = target->getAnimator();
|
||||||
|
///
|
||||||
|
/// When fading in/out, interpolate between the "current" transform (which has possibly been set by the glTF animation loop above)
|
||||||
|
/// and the first (for fading in) or last (for fading out) frame.
|
||||||
|
///
|
||||||
|
for (int i = (int)boneAnimations.size() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
auto animationStatus = boneAnimations[i];
|
||||||
|
|
||||||
|
auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
auto elapsedInMillis = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count());
|
||||||
|
auto elapsedInSecs = elapsedInMillis / 1000.0f;
|
||||||
|
|
||||||
|
// if we're not looping and the amount of time elapsed is greater than the animation duration plus the fade-in/out buffer,
|
||||||
|
// then the animation is completed and we can delete it
|
||||||
|
if (elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
||||||
|
{
|
||||||
|
if(!animationStatus.loop) {
|
||||||
|
boneAnimations.erase(boneAnimations.begin() + i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're fading in, treat elapsedFrames is zero (and fading out, treat elapsedFrames as lengthInFrames)
|
||||||
|
float elapsedInFrames = (elapsedInMillis - (1000 * animationStatus.fadeInInSecs)) / animationStatus.frameLengthInMs;
|
||||||
|
int currFrame = std::floor(elapsedInFrames);
|
||||||
|
int nextFrame = currFrame;
|
||||||
|
|
||||||
|
// offset from the end if reverse
|
||||||
|
if (animationStatus.reverse)
|
||||||
|
{
|
||||||
|
currFrame = animationStatus.lengthInFrames - currFrame;
|
||||||
|
if (currFrame > 0)
|
||||||
|
{
|
||||||
|
nextFrame = currFrame - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextFrame = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (currFrame < animationStatus.lengthInFrames - 1)
|
||||||
|
{
|
||||||
|
nextFrame = currFrame + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextFrame = currFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currFrame = std::clamp(currFrame, 0, animationStatus.lengthInFrames - 1);
|
||||||
|
nextFrame = std::clamp(nextFrame, 0, animationStatus.lengthInFrames - 1);
|
||||||
|
|
||||||
|
float frameDelta = elapsedInFrames - currFrame;
|
||||||
|
|
||||||
|
// linearly interpolate this animation between its last/current frames
|
||||||
|
// this is to avoid jerky animations when the animation framerate is slower than our tick rate
|
||||||
|
|
||||||
|
math::float3 currScale, newScale;
|
||||||
|
math::quatf currRotation, newRotation;
|
||||||
|
math::float3 currTranslation, newTranslation;
|
||||||
|
math::mat4f curr = animationStatus.frameData[currFrame];
|
||||||
|
decomposeMatrix(curr, &currTranslation, &currRotation, &currScale);
|
||||||
|
|
||||||
|
if(frameDelta > 0) {
|
||||||
|
math::mat4f next = animationStatus.frameData[nextFrame];
|
||||||
|
decomposeMatrix(next, &newTranslation, &newRotation, &newScale);
|
||||||
|
newScale = mix(currScale, newScale, frameDelta);
|
||||||
|
newRotation = slerp(currRotation, newRotation, frameDelta);
|
||||||
|
newTranslation = mix(currTranslation, newTranslation, frameDelta);
|
||||||
|
} else {
|
||||||
|
newScale = currScale;
|
||||||
|
newRotation = currRotation;
|
||||||
|
newTranslation = currTranslation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Entity joint = target->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
|
||||||
|
|
||||||
|
// now calculate the fade out/in delta
|
||||||
|
// if we're fading in, this will be 0.0 at the start of the fade and 1.0 at the end
|
||||||
|
auto fadeDelta = elapsedInSecs / animationStatus.fadeInInSecs;
|
||||||
|
|
||||||
|
// // if we're fading out, this will be 1.0 at the start of the fade and 0.0 at the end
|
||||||
|
if(fadeDelta > 1.0f) {
|
||||||
|
fadeDelta = 1 - ((elapsedInSecs - animationStatus.durationInSecs - animationStatus.fadeInInSecs) / animationStatus.fadeOutInSecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fadeDelta = std::clamp(fadeDelta, 0.0f, animationStatus.maxDelta);
|
||||||
|
|
||||||
|
auto jointTransform = mTransformManager.getInstance(joint);
|
||||||
|
|
||||||
|
// linearly interpolate this animation between its current (interpolated) frame and the current transform (i.e. as set by the gltf frame)
|
||||||
|
// // if we are fading in or out, apply a delta
|
||||||
|
if (fadeDelta >= 0.0f && fadeDelta <= 1.0f) {
|
||||||
|
math::float3 fadeScale;
|
||||||
|
math::quatf fadeRotation;
|
||||||
|
math::float3 fadeTranslation;
|
||||||
|
auto currentTransform = mTransformManager.getTransform(jointTransform);
|
||||||
|
decomposeMatrix(currentTransform, &fadeTranslation, &fadeRotation, &fadeScale);
|
||||||
|
newScale = mix(fadeScale, newScale, fadeDelta);
|
||||||
|
newRotation = slerp(fadeRotation, newRotation, fadeDelta);
|
||||||
|
newTranslation = mix(fadeTranslation, newTranslation, fadeDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTransformManager.setTransform(jointTransform, composeMatrix(newTranslation, newRotation, newScale));
|
||||||
|
|
||||||
|
animator->updateBoneMatrices();
|
||||||
|
|
||||||
|
if (animationStatus.loop && elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
||||||
|
{
|
||||||
|
animationStatus.start = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include "components/AnimationComponentManager.hpp"
|
||||||
|
|
||||||
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
namespace thermion
|
||||||
|
{
|
||||||
|
|
||||||
|
void GltfAnimationComponentManager::addAnimationComponent(FilamentInstance *target) {
|
||||||
|
if(!hasComponent(target->getRoot())) {
|
||||||
|
EntityInstanceBase::Type componentInstance = addComponent(target->getRoot());
|
||||||
|
this->elementAt<0>(componentInstance) = { target };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GltfAnimationComponentManager::removeAnimationComponent(FilamentInstance *target) {
|
||||||
|
if(hasComponent(target->getRoot())) {
|
||||||
|
removeComponent(target->getRoot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GltfAnimationComponentManager::update() {
|
||||||
|
TRACE("Updating with %d components", getComponentCount());
|
||||||
|
for (auto it = begin(); it < end(); it++)
|
||||||
|
{
|
||||||
|
const auto &entity = getEntity(it);
|
||||||
|
|
||||||
|
auto componentInstance = getInstance(entity);
|
||||||
|
auto &animationComponent = elementAt<0>(componentInstance);
|
||||||
|
|
||||||
|
auto target = animationComponent.target;
|
||||||
|
auto animator = target->getAnimator();
|
||||||
|
auto &gltfAnimations = animationComponent.animations;
|
||||||
|
|
||||||
|
for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
auto animationStatus = gltfAnimations[i];
|
||||||
|
|
||||||
|
auto elapsedInSecs = animationStatus.startOffset + float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
|
||||||
|
|
||||||
|
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
|
||||||
|
{
|
||||||
|
animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001);
|
||||||
|
animator->updateBoneMatrices();
|
||||||
|
gltfAnimations.erase(gltfAnimations.begin() + i);
|
||||||
|
animationComponent.fadeGltfAnimationIndex = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
animator->applyAnimation(animationStatus.index, elapsedInSecs);
|
||||||
|
|
||||||
|
if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration)
|
||||||
|
{
|
||||||
|
// cross-fade
|
||||||
|
auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs;
|
||||||
|
auto alpha = elapsedInSecs / animationComponent.fadeDuration;
|
||||||
|
animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animator->updateBoneMatrices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void AnimationComponentManager::addGltfAnimationComponent(FilamentInstance *target) {
|
||||||
|
// if(!hasComponent(target->getRoot())) {
|
||||||
|
// EntityInstanceBase::Type componentInstance;
|
||||||
|
// componentInstance = addComponent(instance->getRoot());
|
||||||
|
// this->elementAt<0>(componentInstance) = GltfAnimationComponent animationComponent { target };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // auto entity = std::get<Entity>(target);
|
||||||
|
// // if(!hasComponent(entity)) {
|
||||||
|
// // componentInstance = addComponent(entity);
|
||||||
|
// // this->elementAt<0>(componentInstance) = animationComponent;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
|
||||||
|
// void AnimationComponentManager::removeAnimationComponent(std::variant<FilamentInstance *, Entity> target)
|
||||||
|
// {
|
||||||
|
// AnimationComponent animationComponent;
|
||||||
|
// animationComponent.target = target;
|
||||||
|
// EntityInstanceBase::Type componentInstance;
|
||||||
|
// if (std::holds_alternative<FilamentInstance *>(target))
|
||||||
|
// {
|
||||||
|
// auto instance = std::get<FilamentInstance *>(target);
|
||||||
|
// if(hasComponent(instance->getRoot())) {
|
||||||
|
// removeComponent(instance->getRoot());
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// auto entity = std::get<Entity>(target);
|
||||||
|
// if(hasComponent(entity)) {
|
||||||
|
// removeComponent(entity);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void AnimationComponentManager::update()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// for (auto it = begin(); it < end(); it++)
|
||||||
|
// {
|
||||||
|
// const auto &entity = getEntity(it);
|
||||||
|
|
||||||
|
// auto componentInstance = getInstance(entity);
|
||||||
|
// auto &animationComponent = elementAt<0>(componentInstance);
|
||||||
|
|
||||||
|
// auto &morphAnimations = animationComponent.morphAnimations;
|
||||||
|
|
||||||
|
// if (std::holds_alternative<FilamentInstance *>(animationComponent.target))
|
||||||
|
// {
|
||||||
|
// auto target = std::get<FilamentInstance *>(animationComponent.target);
|
||||||
|
// auto animator = target->getAnimator();
|
||||||
|
// auto &gltfAnimations = animationComponent.gltfAnimations;
|
||||||
|
// auto &boneAnimations = animationComponent.boneAnimations;
|
||||||
|
|
||||||
|
// if(gltfAnimations.size() > 0) {
|
||||||
|
// for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
// auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
// auto animationStatus = animationComponent.gltfAnimations[i];
|
||||||
|
|
||||||
|
// auto elapsedInSecs = animationStatus.startOffset + float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
|
||||||
|
|
||||||
|
// if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
|
||||||
|
// {
|
||||||
|
// animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001);
|
||||||
|
// animator->updateBoneMatrices();
|
||||||
|
// gltfAnimations.erase(gltfAnimations.begin() + i);
|
||||||
|
// animationComponent.fadeGltfAnimationIndex = -1;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// animator->applyAnimation(animationStatus.index, elapsedInSecs);
|
||||||
|
|
||||||
|
// if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration)
|
||||||
|
// {
|
||||||
|
// // cross-fade
|
||||||
|
// auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs;
|
||||||
|
// auto alpha = elapsedInSecs / animationComponent.fadeDuration;
|
||||||
|
// animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// animator->updateBoneMatrices();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ///
|
||||||
|
// /// When fading in/out, interpolate between the "current" transform (which has possibly been set by the glTF animation loop above)
|
||||||
|
// /// and the first (for fading in) or last (for fading out) frame.
|
||||||
|
// ///
|
||||||
|
// for (int i = (int)boneAnimations.size() - 1; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
// auto animationStatus = boneAnimations[i];
|
||||||
|
|
||||||
|
// auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
// auto elapsedInMillis = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count());
|
||||||
|
// auto elapsedInSecs = elapsedInMillis / 1000.0f;
|
||||||
|
|
||||||
|
// // if we're not looping and the amount of time elapsed is greater than the animation duration plus the fade-in/out buffer,
|
||||||
|
// // then the animation is completed and we can delete it
|
||||||
|
// if (elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
||||||
|
// {
|
||||||
|
// if(!animationStatus.loop) {
|
||||||
|
// boneAnimations.erase(boneAnimations.begin() + i);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // if we're fading in, treat elapsedFrames is zero (and fading out, treat elapsedFrames as lengthInFrames)
|
||||||
|
// float elapsedInFrames = (elapsedInMillis - (1000 * animationStatus.fadeInInSecs)) / animationStatus.frameLengthInMs;
|
||||||
|
// int currFrame = std::floor(elapsedInFrames);
|
||||||
|
// int nextFrame = currFrame;
|
||||||
|
|
||||||
|
// // offset from the end if reverse
|
||||||
|
// if (animationStatus.reverse)
|
||||||
|
// {
|
||||||
|
// currFrame = animationStatus.lengthInFrames - currFrame;
|
||||||
|
// if (currFrame > 0)
|
||||||
|
// {
|
||||||
|
// nextFrame = currFrame - 1;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// nextFrame = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (currFrame < animationStatus.lengthInFrames - 1)
|
||||||
|
// {
|
||||||
|
// nextFrame = currFrame + 1;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// nextFrame = currFrame;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// currFrame = std::clamp(currFrame, 0, animationStatus.lengthInFrames - 1);
|
||||||
|
// nextFrame = std::clamp(nextFrame, 0, animationStatus.lengthInFrames - 1);
|
||||||
|
|
||||||
|
// float frameDelta = elapsedInFrames - currFrame;
|
||||||
|
|
||||||
|
// // linearly interpolate this animation between its last/current frames
|
||||||
|
// // this is to avoid jerky animations when the animation framerate is slower than our tick rate
|
||||||
|
|
||||||
|
// math::float3 currScale, newScale;
|
||||||
|
// math::quatf currRotation, newRotation;
|
||||||
|
// math::float3 currTranslation, newTranslation;
|
||||||
|
// math::mat4f curr = animationStatus.frameData[currFrame];
|
||||||
|
// decomposeMatrix(curr, &currTranslation, &currRotation, &currScale);
|
||||||
|
|
||||||
|
// if(frameDelta > 0) {
|
||||||
|
// math::mat4f next = animationStatus.frameData[nextFrame];
|
||||||
|
// decomposeMatrix(next, &newTranslation, &newRotation, &newScale);
|
||||||
|
// newScale = mix(currScale, newScale, frameDelta);
|
||||||
|
// newRotation = slerp(currRotation, newRotation, frameDelta);
|
||||||
|
// newTranslation = mix(currTranslation, newTranslation, frameDelta);
|
||||||
|
// } else {
|
||||||
|
// newScale = currScale;
|
||||||
|
// newRotation = currRotation;
|
||||||
|
// newTranslation = currTranslation;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const Entity joint = target->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
|
||||||
|
|
||||||
|
// // now calculate the fade out/in delta
|
||||||
|
// // if we're fading in, this will be 0.0 at the start of the fade and 1.0 at the end
|
||||||
|
// auto fadeDelta = elapsedInSecs / animationStatus.fadeInInSecs;
|
||||||
|
|
||||||
|
// // // if we're fading out, this will be 1.0 at the start of the fade and 0.0 at the end
|
||||||
|
// if(fadeDelta > 1.0f) {
|
||||||
|
// fadeDelta = 1 - ((elapsedInSecs - animationStatus.durationInSecs - animationStatus.fadeInInSecs) / animationStatus.fadeOutInSecs);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fadeDelta = std::clamp(fadeDelta, 0.0f, animationStatus.maxDelta);
|
||||||
|
|
||||||
|
// auto jointTransform = _transformManager.getInstance(joint);
|
||||||
|
|
||||||
|
// // linearly interpolate this animation between its current (interpolated) frame and the current transform (i.e. as set by the gltf frame)
|
||||||
|
// // // if we are fading in or out, apply a delta
|
||||||
|
// if (fadeDelta >= 0.0f && fadeDelta <= 1.0f) {
|
||||||
|
// math::float3 fadeScale;
|
||||||
|
// math::quatf fadeRotation;
|
||||||
|
// math::float3 fadeTranslation;
|
||||||
|
// auto currentTransform = _transformManager.getTransform(jointTransform);
|
||||||
|
// decomposeMatrix(currentTransform, &fadeTranslation, &fadeRotation, &fadeScale);
|
||||||
|
// newScale = mix(fadeScale, newScale, fadeDelta);
|
||||||
|
// newRotation = slerp(fadeRotation, newRotation, fadeDelta);
|
||||||
|
// newTranslation = mix(fadeTranslation, newTranslation, fadeDelta);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _transformManager.setTransform(jointTransform, composeMatrix(newTranslation, newRotation, newScale));
|
||||||
|
|
||||||
|
// animator->updateBoneMatrices();
|
||||||
|
|
||||||
|
// if (animationStatus.loop && elapsedInSecs >= (animationStatus.durationInSecs + animationStatus.fadeInInSecs + animationStatus.fadeOutInSecs))
|
||||||
|
// {
|
||||||
|
// animationStatus.start = now;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for (int i = (int)morphAnimations.size() - 1; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
// auto animationStatus = morphAnimations[i];
|
||||||
|
|
||||||
|
// auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
|
||||||
|
|
||||||
|
// if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
|
||||||
|
// {
|
||||||
|
// morphAnimations.erase(morphAnimations.begin() + i);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames;
|
||||||
|
// // offset from the end if reverse
|
||||||
|
// if (animationStatus.reverse)
|
||||||
|
// {
|
||||||
|
// frameNumber = animationStatus.lengthInFrames - frameNumber;
|
||||||
|
// }
|
||||||
|
// auto baseOffset = frameNumber * animationStatus.morphIndices.size();
|
||||||
|
// for (int i = 0; i < animationStatus.morphIndices.size(); i++)
|
||||||
|
// {
|
||||||
|
// auto morphIndex = animationStatus.morphIndices[i];
|
||||||
|
// // set the weights appropriately
|
||||||
|
// _renderableManager.setMorphWeights(
|
||||||
|
// _renderableManager.getInstance(animationStatus.meshTarget),
|
||||||
|
// animationStatus.frameData.data() + baseOffset + i,
|
||||||
|
// 1,
|
||||||
|
// morphIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include "components/AnimationComponentManager.hpp"
|
||||||
|
|
||||||
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
namespace thermion
|
||||||
|
{
|
||||||
|
|
||||||
|
void MorphAnimationComponentManager::addAnimationComponent(utils::Entity target) {
|
||||||
|
if(!hasComponent(target)) {
|
||||||
|
EntityInstanceBase::Type componentInstance = addComponent(target);
|
||||||
|
this->elementAt<0>(componentInstance) = MorphAnimationComponent { };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphAnimationComponentManager::removeAnimationComponent(utils::Entity target) {
|
||||||
|
if(hasComponent(target)) {
|
||||||
|
removeComponent(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MorphAnimationComponentManager::update() {
|
||||||
|
TRACE("Updating %d morph animation components", getComponentCount());
|
||||||
|
for (auto it = begin(); it < end(); it++)
|
||||||
|
{
|
||||||
|
const auto &entity = getEntity(it);
|
||||||
|
|
||||||
|
auto componentInstance = getInstance(entity);
|
||||||
|
|
||||||
|
auto &animationComponent = elementAt<0>(componentInstance);
|
||||||
|
auto &animations = animationComponent.animations;
|
||||||
|
|
||||||
|
TRACE("Component has %d animations", animations.size());
|
||||||
|
|
||||||
|
for (int i = (int)animations.size() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
auto &animation = animationComponent.animations[i];
|
||||||
|
|
||||||
|
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animation.start).count()) / 1000.0f;
|
||||||
|
|
||||||
|
if (!animation.loop && elapsedInSecs >= animation.durationInSecs)
|
||||||
|
{
|
||||||
|
animations.erase(animations.begin() + i);
|
||||||
|
TRACE("Animation %d completed", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animation.frameLengthInMs) % animation.lengthInFrames;
|
||||||
|
// offset from the end if reverse
|
||||||
|
if (animation.reverse)
|
||||||
|
{
|
||||||
|
frameNumber = animation.lengthInFrames - frameNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto baseOffset = frameNumber * animation.morphIndices.size();
|
||||||
|
for (int i = 0; i < animation.morphIndices.size(); i++)
|
||||||
|
{
|
||||||
|
auto morphIndex = animation.morphIndices[i];
|
||||||
|
auto renderableInstance = mRenderableManager.getInstance(entity);
|
||||||
|
|
||||||
|
mRenderableManager.setMorphWeights(
|
||||||
|
renderableInstance,
|
||||||
|
animation.frameData.data() + baseOffset + i,
|
||||||
|
1,
|
||||||
|
morphIndex);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
#include "components/AnimationComponentManager.hpp"
|
||||||
|
#include "components/AnimationComponentManager.hpp"
|
||||||
#include "scene/AnimationManager.hpp"
|
#include "scene/AnimationManager.hpp"
|
||||||
#include "scene/SceneAsset.hpp"
|
#include "scene/SceneAsset.hpp"
|
||||||
#include "scene/GltfSceneAssetInstance.hpp"
|
#include "scene/GltfSceneAssetInstance.hpp"
|
||||||
@@ -25,12 +27,9 @@ namespace thermion
|
|||||||
{
|
{
|
||||||
auto &transformManager = _engine->getTransformManager();
|
auto &transformManager = _engine->getTransformManager();
|
||||||
auto &renderableManager = _engine->getRenderableManager();
|
auto &renderableManager = _engine->getRenderableManager();
|
||||||
_animationComponentManager = std::make_unique<AnimationComponentManager>(transformManager, renderableManager);
|
_gltfAnimationComponentManager = std::make_unique<GltfAnimationComponentManager>(transformManager, renderableManager);
|
||||||
}
|
_morphAnimationComponentManager = std::make_unique<MorphAnimationComponentManager>(transformManager, renderableManager);
|
||||||
|
_boneAnimationComponentManager = std::make_unique<BoneAnimationComponentManager>(transformManager, renderableManager);
|
||||||
AnimationManager::~AnimationManager()
|
|
||||||
{
|
|
||||||
_animationComponentManager = std::nullptr_t();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationManager::setMorphAnimationBuffer(
|
bool AnimationManager::setMorphAnimationBuffer(
|
||||||
@@ -41,16 +40,20 @@ namespace thermion
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
float frameLengthInMs)
|
float frameLengthInMs)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::lock_guard lock(_mutex);
|
std::lock_guard lock(_mutex);
|
||||||
|
|
||||||
if (!_animationComponentManager->hasComponent(entity))
|
if (!_morphAnimationComponentManager->hasComponent(entity))
|
||||||
{
|
{
|
||||||
_animationComponentManager->addAnimationComponent(entity);
|
_morphAnimationComponentManager->addAnimationComponent(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto animationComponentInstance = _morphAnimationComponentManager->getInstance(entity);
|
||||||
|
auto &animationComponent = _morphAnimationComponentManager->elementAt<0>(animationComponentInstance);
|
||||||
|
auto &morphAnimations = animationComponent.animations;
|
||||||
|
|
||||||
MorphAnimation morphAnimation;
|
MorphAnimation morphAnimation;
|
||||||
|
|
||||||
morphAnimation.meshTarget = entity;
|
|
||||||
morphAnimation.frameData.clear();
|
morphAnimation.frameData.clear();
|
||||||
morphAnimation.frameData.insert(
|
morphAnimation.frameData.insert(
|
||||||
morphAnimation.frameData.begin(),
|
morphAnimation.frameData.begin(),
|
||||||
@@ -65,15 +68,12 @@ namespace thermion
|
|||||||
morphAnimation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f;
|
morphAnimation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f;
|
||||||
|
|
||||||
morphAnimation.start = high_resolution_clock::now();
|
morphAnimation.start = high_resolution_clock::now();
|
||||||
morphAnimation.lengthInFrames = static_cast<int>(
|
morphAnimation.lengthInFrames = numFrames;
|
||||||
morphAnimation.durationInSecs * 1000.0f /
|
|
||||||
frameLengthInMs);
|
|
||||||
|
|
||||||
auto animationComponentInstance = _animationComponentManager->getInstance(entity);
|
|
||||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
|
||||||
auto &morphAnimations = animationComponent.morphAnimations;
|
|
||||||
|
|
||||||
morphAnimations.emplace_back(morphAnimation);
|
morphAnimations.emplace_back(morphAnimation);
|
||||||
|
|
||||||
|
auto& foo = morphAnimations[morphAnimations.size() - 1];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +82,9 @@ namespace thermion
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(_mutex);
|
std::lock_guard lock(_mutex);
|
||||||
|
|
||||||
auto animationComponentInstance = _animationComponentManager->getInstance(entity);
|
auto animationComponentInstance = _morphAnimationComponentManager->getInstance(entity);
|
||||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
auto &animationComponent = _morphAnimationComponentManager->elementAt<0>(animationComponentInstance);
|
||||||
auto &morphAnimations = animationComponent.morphAnimations;
|
auto &morphAnimations = animationComponent.animations;
|
||||||
morphAnimations.clear();
|
morphAnimations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,17 +309,17 @@ namespace thermion
|
|||||||
animation.fadeInInSecs = fadeInInSecs;
|
animation.fadeInInSecs = fadeInInSecs;
|
||||||
animation.maxDelta = maxDelta;
|
animation.maxDelta = maxDelta;
|
||||||
animation.skinIndex = skinIndex;
|
animation.skinIndex = skinIndex;
|
||||||
if (!_animationComponentManager->hasComponent(instance->getInstance()->getRoot()))
|
if (!_boneAnimationComponentManager->hasComponent(instance->getInstance()->getRoot()))
|
||||||
{
|
{
|
||||||
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getInstance()->getRoot());
|
auto animationComponentInstance = _boneAnimationComponentManager->getInstance(instance->getInstance()->getRoot());
|
||||||
|
|
||||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
auto &animationComponent = _boneAnimationComponentManager->elementAt<0>(animationComponentInstance);
|
||||||
auto &boneAnimations = animationComponent.boneAnimations;
|
// auto &boneAnimations = animationComponent.boneAnimations;
|
||||||
|
|
||||||
boneAnimations.emplace_back(animation);
|
// boneAnimations.emplace_back(animation);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -334,27 +334,30 @@ namespace thermion
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_animationComponentManager->hasComponent(instance->getEntity()))
|
if (!_gltfAnimationComponentManager->hasComponent(instance->getEntity()))
|
||||||
{
|
{
|
||||||
|
_gltfAnimationComponentManager->addComponent(instance->getEntity());
|
||||||
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getEntity());
|
auto animationComponentInstance = _gltfAnimationComponentManager->getInstance(instance->getEntity());
|
||||||
|
|
||||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
auto &animationComponent = _gltfAnimationComponentManager->elementAt<0>(animationComponentInstance);
|
||||||
|
|
||||||
|
animationComponent.target = instance->getInstance();
|
||||||
|
|
||||||
if (replaceActive)
|
if (replaceActive)
|
||||||
{
|
{
|
||||||
if (animationComponent.gltfAnimations.size() > 0)
|
if (animationComponent.animations.size() > 0)
|
||||||
{
|
{
|
||||||
auto &last = animationComponent.gltfAnimations.back();
|
auto &last = animationComponent.animations.back();
|
||||||
animationComponent.fadeGltfAnimationIndex = last.index;
|
animationComponent.fadeGltfAnimationIndex = last.index;
|
||||||
animationComponent.fadeDuration = crossfade;
|
animationComponent.fadeDuration = crossfade;
|
||||||
auto now = high_resolution_clock::now();
|
auto now = high_resolution_clock::now();
|
||||||
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last.start).count()) / 1000.0f;
|
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last.start).count()) / 1000.0f;
|
||||||
animationComponent.fadeOutAnimationStart = elapsedInSecs;
|
animationComponent.fadeOutAnimationStart = elapsedInSecs;
|
||||||
animationComponent.gltfAnimations.clear();
|
animationComponent.animations.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -384,9 +387,9 @@ namespace thermion
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
// don't play the animation if it's already running
|
// don't play the animation if it's already running
|
||||||
for (int i = 0; i < animationComponent.gltfAnimations.size(); i++)
|
for (int i = 0; i < animationComponent.animations.size(); i++)
|
||||||
{
|
{
|
||||||
if (animationComponent.gltfAnimations[i].index == index)
|
if (animationComponent.animations[i].index == index)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@@ -394,22 +397,22 @@ namespace thermion
|
|||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
animationComponent.gltfAnimations.push_back(animation);
|
animationComponent.animations.push_back(animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationManager::stopGltfAnimation(GltfSceneAssetInstance *instance, int index)
|
void AnimationManager::stopGltfAnimation(GltfSceneAssetInstance *instance, int index)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getEntity());
|
auto animationComponentInstance = _gltfAnimationComponentManager->getInstance(instance->getEntity());
|
||||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
auto &animationComponent = _gltfAnimationComponentManager->elementAt<0>(animationComponentInstance);
|
||||||
|
|
||||||
auto erased = std::remove_if(animationComponent.gltfAnimations.begin(),
|
auto erased = std::remove_if(animationComponent.animations.begin(),
|
||||||
animationComponent.gltfAnimations.end(),
|
animationComponent.animations.end(),
|
||||||
[=](GltfAnimation &anim)
|
[=](GltfAnimation &anim)
|
||||||
{ return anim.index == index; });
|
{ return anim.index == index; });
|
||||||
animationComponent.gltfAnimations.erase(erased,
|
animationComponent.animations.erase(erased,
|
||||||
animationComponent.gltfAnimations.end());
|
animationComponent.animations.end());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +420,7 @@ namespace thermion
|
|||||||
{
|
{
|
||||||
RenderableManager &rm = _engine->getRenderableManager();
|
RenderableManager &rm = _engine->getRenderableManager();
|
||||||
auto renderableInstance = rm.getInstance(entity);
|
auto renderableInstance = rm.getInstance(entity);
|
||||||
|
|
||||||
rm.setMorphWeights(
|
rm.setMorphWeights(
|
||||||
renderableInstance,
|
renderableInstance,
|
||||||
weights,
|
weights,
|
||||||
@@ -477,7 +481,9 @@ namespace thermion
|
|||||||
void AnimationManager::update(uint64_t frameTimeInNanos)
|
void AnimationManager::update(uint64_t frameTimeInNanos)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(_mutex);
|
std::lock_guard lock(_mutex);
|
||||||
_animationComponentManager->update();
|
_gltfAnimationComponentManager->update();
|
||||||
|
_morphAnimationComponentManager->update();
|
||||||
|
_boneAnimationComponentManager->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
math::mat4f AnimationManager::getInverseBindMatrix(GltfSceneAssetInstance *instance, int skinIndex, int boneIndex)
|
math::mat4f AnimationManager::getInverseBindMatrix(GltfSceneAssetInstance *instance, int skinIndex, int boneIndex)
|
||||||
@@ -507,15 +513,37 @@ namespace thermion
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationManager::addAnimationComponent(EntityId entity)
|
bool AnimationManager::addGltfAnimationComponent(GltfSceneAssetInstance *instance)
|
||||||
{
|
{
|
||||||
_animationComponentManager->addAnimationComponent(utils::Entity::import(entity));
|
_gltfAnimationComponentManager->addAnimationComponent(instance->getInstance());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationManager::removeAnimationComponent(EntityId entity)
|
void AnimationManager::removeGltfAnimationComponent(GltfSceneAssetInstance *instance)
|
||||||
{
|
{
|
||||||
_animationComponentManager->removeComponent(utils::Entity::import(entity));
|
_gltfAnimationComponentManager->removeAnimationComponent(instance->getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationManager::addBoneAnimationComponent(GltfSceneAssetInstance *instance)
|
||||||
|
{
|
||||||
|
_boneAnimationComponentManager->addAnimationComponent(instance->getInstance());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationManager::removeBoneAnimationComponent(GltfSceneAssetInstance *instance)
|
||||||
|
{
|
||||||
|
_boneAnimationComponentManager->removeAnimationComponent(instance->getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationManager::addMorphAnimationComponent(utils::Entity entity)
|
||||||
|
{
|
||||||
|
_morphAnimationComponentManager->addAnimationComponent(entity);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationManager::removeMorphAnimationComponent(utils::Entity entity)
|
||||||
|
{
|
||||||
|
_morphAnimationComponentManager->removeAnimationComponent(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,45 +3,87 @@ import 'dart:typed_data';
|
|||||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:thermion_dart/src/bindings/bindings.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/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'helpers.dart';
|
import 'helpers.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
final testHelper = TestHelper("animation");
|
final testHelper = TestHelper("animation");
|
||||||
await testHelper.setup();
|
await testHelper.setup();
|
||||||
group('morph animation tests', () {
|
|
||||||
test('retrieve morph target names', () async {
|
|
||||||
await testHelper.withViewer((viewer) async {
|
|
||||||
final cube = await viewer.loadGltf(
|
|
||||||
"${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
|
||||||
final childEntities = await cube.getChildEntities();
|
|
||||||
var morphTargets =
|
|
||||||
await cube.getMorphTargetNames(entity: childEntities.first);
|
|
||||||
expect(morphTargets.length, 1);
|
|
||||||
expect(morphTargets.first, "Key 1");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('set morph target weights', () async {
|
test('get morph target names', () async {
|
||||||
await testHelper.withViewer((viewer) async {
|
await testHelper.withViewer((viewer) async {
|
||||||
final cube = await viewer.loadGltf(
|
var cube = await viewer.loadGltf("${testHelper.testDir}/assets/cube.glb");
|
||||||
"${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
var morphTargets = await cube.getMorphTargetNames();
|
||||||
print(await cube.getChildEntityNames());
|
expect(morphTargets.length, 0);
|
||||||
await viewer.addToScene(cube);
|
|
||||||
await testHelper.capture(viewer.view, "cube_no_morph");
|
|
||||||
|
|
||||||
final childEntities = await cube.getChildEntities();
|
var childEntities = await cube.getChildEntities();
|
||||||
|
var childEntity = childEntities.first;
|
||||||
|
|
||||||
var morphData = MorphAnimationData(
|
morphTargets = await cube.getMorphTargetNames(entity: childEntity);
|
||||||
Float32List.fromList([1.0]), ["Key 1"],
|
expect(morphTargets.length, 0);
|
||||||
frameLengthInMs: 1000.0 / 60.0);
|
|
||||||
await cube.addAnimationComponent(childEntities.first);
|
cube = await viewer
|
||||||
await cube.setMorphAnimationData(morphData);
|
.loadGltf("${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
||||||
await viewer.render();
|
morphTargets = await cube.getMorphTargetNames();
|
||||||
await testHelper.capture(viewer.view, "cube_with_morph");
|
expect(morphTargets.length, 0);
|
||||||
}, bg: kRed);
|
|
||||||
|
childEntities = await cube.getChildEntities();
|
||||||
|
|
||||||
|
morphTargets =
|
||||||
|
await cube.getMorphTargetNames(entity: childEntities.first);
|
||||||
|
expect(morphTargets.length, 1);
|
||||||
|
expect(morphTargets.first, "Key 1");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('set morph target weights', () async {
|
||||||
|
await testHelper.withViewer((viewer) async {
|
||||||
|
final cube = await viewer.loadGltf(
|
||||||
|
"${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
||||||
|
|
||||||
|
await viewer.addToScene(cube);
|
||||||
|
|
||||||
|
await testHelper.capture(viewer.view, "cube_no_morph");
|
||||||
|
|
||||||
|
await cube.setMorphTargetWeights((await cube.getChildEntities()).first, [1.0]);
|
||||||
|
await testHelper.capture(viewer.view, "cube_with_morph");
|
||||||
|
|
||||||
|
}, bg:kRed, cameraPosition: Vector3(3, 2, 6));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('set morph target animation', () async {
|
||||||
|
await testHelper.withViewer((viewer) async {
|
||||||
|
final cube = await viewer.loadGltf(
|
||||||
|
"${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
||||||
|
|
||||||
|
await viewer.addToScene(cube);
|
||||||
|
|
||||||
|
var morphData = MorphAnimationData(Float32List.fromList([1.0]), ["Key 1"],
|
||||||
|
frameLengthInMs: 1000.0 / 60.0);
|
||||||
|
|
||||||
|
await cube.setMorphAnimationData(morphData);
|
||||||
|
await viewer.render();
|
||||||
|
await testHelper.capture(viewer.view, "cube_with_morph_animation");
|
||||||
|
|
||||||
|
}, bg:kRed, cameraPosition: Vector3(3, 2, -6));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get gltf animation names', () async {
|
||||||
|
await testHelper.withViewer((viewer) async {
|
||||||
|
final cube = await viewer
|
||||||
|
.loadGltf("${testHelper.testDir}/assets/cube_with_morph_targets.glb");
|
||||||
|
|
||||||
|
await viewer.addToScene(cube);
|
||||||
|
await testHelper.capture(viewer.view, "gltf_animation_stopped");
|
||||||
|
|
||||||
|
final animationNames = await cube.getGltfAnimationNames();
|
||||||
|
|
||||||
|
expect(animationNames.first, "CubeAction");
|
||||||
|
|
||||||
|
await cube.playGltfAnimation(0);
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
await viewer.render();
|
||||||
|
await testHelper.capture(viewer.view, "gltf_animation_started");
|
||||||
|
}, bg: kRed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user