From ce425a44f5f6c22c57c45e6ac9a012d0e6839731 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 26 Apr 2024 11:03:30 +0800 Subject: [PATCH] move animations to animation_tools_dart --- .../animations/animation_builder.dart | 155 ------------------ lib/filament/animations/animation_data.dart | 61 ------- lib/filament/animations/bvh.dart | 138 ---------------- 3 files changed, 354 deletions(-) delete mode 100644 lib/filament/animations/animation_builder.dart delete mode 100644 lib/filament/animations/animation_data.dart delete mode 100644 lib/filament/animations/bvh.dart diff --git a/lib/filament/animations/animation_builder.dart b/lib/filament/animations/animation_builder.dart deleted file mode 100644 index c7994e6e..00000000 --- a/lib/filament/animations/animation_builder.dart +++ /dev/null @@ -1,155 +0,0 @@ -import 'package:flutter_filament/filament/animations/animation_data.dart'; -import 'package:vector_math/vector_math.dart'; - -class AnimationBuilder { - // BoneAnimationData? BoneAnimationData; - double _frameLengthInMs = 0; - double _duration = 0; - - double? _interpMorphStart; - double? _interpMorphEnd; - double? _interpMorphStartValue; - double? _interpMorphEndValue; - - // List? _BoneAnimationDatas = null; - - String meshName; - late List availableMorphs; - late List _morphTargets; - - AnimationBuilder( - {required this.availableMorphs, - required this.meshName, - required int framerate}) { - _frameLengthInMs = 1000 / framerate; - } - - MorphAnimationData build() { - if (availableMorphs.isEmpty || _duration == 0 || _frameLengthInMs == 0) { - throw Exception(); - } - - int numFrames = _duration * 1000 ~/ _frameLengthInMs; - - final morphData = - List.filled((numFrames * _morphTargets.length).toInt(), 0.0); - - var frameStart = (_interpMorphStart! * 1000) ~/ _frameLengthInMs; - var frameEnd = (_interpMorphEnd! * 1000) ~/ _frameLengthInMs; - - for (int i = frameStart; i < frameEnd; i++) { - var linear = (i - frameStart) / frameEnd; - - var val = ((1 - linear) * _interpMorphStartValue!) + - (linear * _interpMorphEndValue!); - for (int j = 0; j < _morphTargets.length; j++) { - morphData[(i * _morphTargets.length) + j] = val; - } - } - return MorphAnimationData( - [meshName], - morphData, - _morphTargets.map((i) => availableMorphs[i]).toList(), - _frameLengthInMs); - } - - AnimationBuilder setDuration(double secs) { - _duration = secs; - return this; - } - - AnimationBuilder setMorphTargets(List names) { - _morphTargets = names.map((name) => availableMorphs.indexOf(name)).toList(); - return this; - } - - AnimationBuilder interpolateMorphWeights( - double start, double end, double startValue, double endValue) { - _interpMorphStart = start; - _interpMorphEnd = end; - _interpMorphStartValue = startValue; - _interpMorphEndValue = endValue; - return this; - } - - AnimationBuilder interpolateBoneTransform( - String boneName, - String meshName, - double start, - double end, - Vector3 transStart, - Vector3 transEnd, - Quaternion quatStart, - Quaternion quatEnd) { - var translations = []; - var quats = []; - var frameStart = (start * 1000) ~/ _frameLengthInMs; - var frameEnd = (end * 1000) ~/ _frameLengthInMs; - int numFrames = _duration * 1000 ~/ _frameLengthInMs; - if (frameEnd > numFrames) { - throw Exception(); - } - - for (int i = 0; i < numFrames; i++) { - if (i >= frameStart && i < frameEnd) { - var linear = (i - frameStart) / (frameEnd - frameStart); - - translations.add(Vector3( - ((1 - linear) * transStart.x) + (linear * transEnd.x), - ((1 - linear) * transStart.y) + (linear * transEnd.y), - ((1 - linear) * transStart.z) + (linear * transEnd.z), - )); - - quats.add(Quaternion( - ((1 - linear) * quatStart.x) + (linear * quatEnd.x), - ((1 - linear) * quatStart.y) + (linear * quatEnd.y), - ((1 - linear) * quatStart.z) + (linear * quatEnd.z), - ((1 - linear) * quatStart.w) + (linear * quatEnd.w), - )); - } else { - translations.add(Vector3.zero()); - quats.add(Quaternion.identity()); - } - } - - throw Exception(); - - // var boneFrameData = BoneTransformFrameData(translations, quats); - - // _BoneAnimationDatas ??= []; - - // var frameData = List>.generate( - // numFrames, (index) => boneFrameData.getFrameData(index).toList()); - - // var animData = Float32List.fromList(frameData.expand((x) => x).toList()); - - // _BoneAnimationDatas!.add(DartBoneAnimationData([boneName], [meshName], animData)); - - // return this; - } -} - -class BoneTransformFrameData { - final List translations; - final List quaternions; - - /// - /// The length of [translations] and [quaternions] must be the same; - /// each entry represents the Vec3/Quaternion for the given frame. - /// - BoneTransformFrameData(this.translations, this.quaternions) { - if (translations.length != quaternions.length) { - throw Exception("Length of translation/quaternion frames must match"); - } - } - - Iterable getFrameData(int frame) sync* { - yield translations[frame].x; - yield translations[frame].y; - yield translations[frame].z; - yield quaternions[frame].x; - yield quaternions[frame].y; - yield quaternions[frame].z; - yield quaternions[frame].w; - } -} diff --git a/lib/filament/animations/animation_data.dart b/lib/filament/animations/animation_data.dart deleted file mode 100644 index 42c72178..00000000 --- a/lib/filament/animations/animation_data.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:vector_math/vector_math_64.dart'; - -/// -/// Specifies frame data (i.e. weights) to animate the morph targets contained in [morphTargets] under a mesh named [mesh]. -/// [data] is laid out as numFrames x numMorphTargets. -/// Each frame is [numMorphTargets] in length, where the index of each weight corresponds to the respective index in [morphTargets]. -/// [morphTargets] must be some subset of the actual morph targets under [mesh] (though the order of these does not need to match). -/// -class MorphAnimationData { - final List meshNames; - final List morphTargets; - - final List data; - - MorphAnimationData( - this.meshNames, this.data, this.morphTargets, this.frameLengthInMs) { - assert(data.length == morphTargets.length * numFrames); - } - - int get numMorphTargets => morphTargets.length; - - int get numFrames => data.length ~/ numMorphTargets; - - final double frameLengthInMs; - - Iterable getData(String morphName) sync* { - int index = morphTargets.indexOf(morphName); - if (index == -1) { - throw Exception("No data for morph $morphName"); - } - for (int i = 0; i < numFrames; i++) { - yield data[(i * numMorphTargets) + index]; - } - } -} - -/// -/// Model class for bone animation frame data. -/// To create dynamic/runtime bone animations (as distinct from animations embedded in a glTF asset), create an instance containing the relevant -/// data and pass to the [setBoneAnimation] method on a [FilamentController]. -/// [frameData] is laid out as [locX, locY, locZ, rotW, rotX, rotY, rotZ] -/// -class BoneAnimationData { - final List bones; - final List meshNames; - final List> rotationFrameData; - final List> translationFrameData; - double frameLengthInMs; - final bool isModelSpace; - BoneAnimationData(this.bones, this.meshNames, this.rotationFrameData, - this.translationFrameData, this.frameLengthInMs, - {this.isModelSpace = false}); - - int get numFrames => rotationFrameData.length; - - BoneAnimationData frame(int frame) { - return BoneAnimationData(bones, meshNames, [rotationFrameData[frame]], - [translationFrameData[frame]], frameLengthInMs, - isModelSpace: isModelSpace); - } -} diff --git a/lib/filament/animations/bvh.dart b/lib/filament/animations/bvh.dart deleted file mode 100644 index 800d01ac..00000000 --- a/lib/filament/animations/bvh.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'dart:math'; - -import 'package:flutter_filament/filament/animations/animation_data.dart'; -import 'package:vector_math/vector_math_64.dart'; - -enum RotationMode { ZYX, XYZ } - -enum Axes { Filament, Blender } - -Map _permutations = { - RotationMode.XYZ: Matrix3.identity() - // RotationMode.ZYX:Matrix3.columns([],[],[]), -}; - -class BVHParser { - static Map parseARPRemap(String arpRemapData) { - final remap = {}; - var data = arpRemapData.split("\n"); - for (int i = 0; i < data.length;) { - var srcBone = data[i].split("%")[0]; - if (srcBone.isNotEmpty && srcBone != "None") { - var targetBone = data[i + 1].trim(); - remap[targetBone] = srcBone; - } - i += 5; - } - return remap; - } - - static BoneAnimationData parse(String data, List meshNames, - {Map? remap, - RegExp? boneRegex, - RotationMode rotationMode = RotationMode.ZYX, - Vector3? rootTranslationOffset, - axes = Axes.Filament}) { - // parse the list/hierarchy of bones - final bones = []; - double frameLengthInMs = 0.0; - var iter = data.split("\n").iterator; - while (iter.moveNext()) { - final line = iter.current.trim(); - if (line.startsWith('ROOT') || line.startsWith('JOINT')) { - var bone = line.split(' ')[1]; - if (remap?.containsKey(bone) == true) { - print("Remapping $bone to ${remap![bone]!}"); - bone = remap![bone]!; - } - bones.add(bone); - } else if (line.startsWith('Frame Time:')) { - var frameTime = line.split(' ').last.trim(); - frameLengthInMs = - double.parse(frameTime) * 1000; // Convert to milliseconds - print("Got frame time $frameTime frameLengthInMs $frameLengthInMs"); - break; - } - } - - // filter out any bones that don't match the regexp (if provided) - final boneIndices = boneRegex == null - ? List.generate(bones.length, (index) => index) - : bones.indexed - .where((bone) => boneRegex.hasMatch(bone.$2)) - .map((bone) => bone.$1); - - // the remaining lines contain the actual animation data - // we assume the first six are LOCX LOCY LOCZ ROTZ ROTY ROTX for the root bone, then ROTZ ROTY ROTX for the remainder - final translationData = >[]; - final rotationData = >[]; - while (iter.moveNext()) { - final line = iter.current; - if (line.isEmpty) { - break; - } - var parseResult = _parseFrameData(line, - axes: axes, rootTranslationOffset: rootTranslationOffset); - - rotationData.add( - boneIndices.map((idx) => parseResult.rotationData[idx]).toList()); - translationData.add([parseResult.translationData] + - List.filled(bones.length - 1, Vector3.zero())); - } - - return BoneAnimationData(boneIndices.map((idx) => bones[idx]).toList(), - meshNames, rotationData, translationData, frameLengthInMs, - isModelSpace: true); - } - - static ({List rotationData, Vector3 translationData}) - _parseFrameData(String frameLine, - {Vector3? rootTranslationOffset, axes = Axes.Filament}) { - final frameValues = []; - for (final entry in frameLine.split(RegExp(r'\s+'))) { - if (entry.isNotEmpty) { - frameValues.add(double.parse(entry)); - } - } - // first 3 values are root node position (translation), remainder are ZYX rotatons - // this is hardcoded assumption for BVH files generated by momask only; won't work for any other animations in general - - // Blender exports BVH using same coordinate system (i.e. Z is up, Y points into screen) - // but Filament uses Y-up/Z forward - late Vector3 rootTranslation; - late Vector3 Z, Y, X; - if (axes == Axes.Blender) { - rootTranslation = - Vector3(frameValues[0], frameValues[2], -frameValues[1]); - Z = Vector3(0, 1, 0); - Y = Vector3(0, 0, -1); - X = Vector3(1, 0, 0); - } else { - rootTranslation = Vector3( - frameValues[0], - frameValues[1], - frameValues[2], - ); - Z = Vector3(0, 0, 1); - Y = Vector3(0, 1, 0); - X = Vector3(1, 0, 0); - } - if (rootTranslationOffset != null) { - rootTranslation -= rootTranslationOffset; - } - - List frameData = []; - for (int i = 3; i < frameValues.length; i += 3) { - var raw = frameValues.sublist(i, i + 3); - var z = Quaternion.axisAngle(Z, radians(raw[0])); - var y = Quaternion.axisAngle(Y, radians(raw[1])); - var x = Quaternion.axisAngle(X, radians(raw[2])); - var rotation = z * y * x; - - frameData.add(rotation.normalized()); - } - return (rotationData: frameData, translationData: rootTranslation); - } - - static double radians(double degrees) => degrees * (pi / 180.0); -}