reorder morph animations according to actual mesh morph targets
This commit is contained in:
@@ -49,7 +49,6 @@ class AnimationBuilder {
|
|||||||
meshName,
|
meshName,
|
||||||
morphData,
|
morphData,
|
||||||
_morphTargets.map((i) => availableMorphs[i]).toList(),
|
_morphTargets.map((i) => availableMorphs[i]).toList(),
|
||||||
_morphTargets,
|
|
||||||
_frameLengthInMs);
|
_frameLengthInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,30 @@
|
|||||||
//
|
|
||||||
// A wrapper for morph target animation data.
|
|
||||||
// [data] is laid out as numFrames x numMorphTargets (where each morph target is ordered according to [animatedMorphNames]).
|
|
||||||
// [data] frame data for the morph weights used to animate the morph targets [animatedMorphNames] in mesh [meshName].
|
|
||||||
// the morph targets specified in [morphNames] attached to mesh [meshName].
|
|
||||||
// [animatedMorphNames] must be provided but is not used directly; this is only used to check that the eventual asset being animated contains the same morph targets in the same order.
|
|
||||||
//
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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 {
|
class MorphAnimationData {
|
||||||
final String meshName;
|
final String meshName;
|
||||||
final List<String> animatedMorphNames;
|
final List<String> morphTargets;
|
||||||
final List<int> animatedMorphIndices;
|
|
||||||
|
|
||||||
final List<double> data;
|
final List<double> data;
|
||||||
|
|
||||||
MorphAnimationData(this.meshName, this.data, this.animatedMorphNames,
|
MorphAnimationData(
|
||||||
this.animatedMorphIndices, this.frameLengthInMs) {
|
this.meshName, this.data, this.morphTargets, this.frameLengthInMs) {
|
||||||
assert(data.length == animatedMorphNames.length * numFrames);
|
assert(data.length == morphTargets.length * numFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get numMorphTargets => animatedMorphNames.length;
|
int get numMorphTargets => morphTargets.length;
|
||||||
|
|
||||||
int get numFrames => data.length ~/ numMorphTargets;
|
int get numFrames => data.length ~/ numMorphTargets;
|
||||||
|
|
||||||
final double frameLengthInMs;
|
final double frameLengthInMs;
|
||||||
|
|
||||||
Iterable<double> getData(String morphName) sync* {
|
Iterable<double> getData(String morphName) sync* {
|
||||||
int index = animatedMorphNames.indexOf(morphName);
|
int index = morphTargets.indexOf(morphName);
|
||||||
for (int i = 0; i < numFrames; i++) {
|
for (int i = 0; i < numFrames; i++) {
|
||||||
yield data[(i * numMorphTargets) + index];
|
yield data[(i * numMorphTargets) + index];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,10 +229,10 @@ abstract class FilamentController {
|
|||||||
FilamentEntity entity, int animationIndex);
|
FilamentEntity entity, int animationIndex);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Create/start a dynamic morph target animation for [asset].
|
/// Animate the morph targets in [entity]. See [MorphTargetAnimation] for an explanation as to how to construct the animation frame data.
|
||||||
/// Animates morph target weights/bone transforms (where each frame requires a duration of [frameLengthInMs].
|
/// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity],
|
||||||
/// [morphWeights] is a list of doubles in frame-major format.
|
/// throwing an exception if any cannot be found.
|
||||||
/// Each frame is [numWeights] in length, and each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
|
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
|
||||||
///
|
///
|
||||||
Future setMorphAnimationData(
|
Future setMorphAnimationData(
|
||||||
FilamentEntity entity, MorphAnimationData animation);
|
FilamentEntity entity, MorphAnimationData animation);
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future setMorphAnimationData(
|
Future setMorphAnimationData(
|
||||||
FilamentEntity asset, MorphAnimationData animation) async {
|
FilamentEntity entity, MorphAnimationData animation) async {
|
||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
@@ -580,14 +580,27 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
dataPtr.elementAt(i).value = animation.data[i];
|
dataPtr.elementAt(i).value = animation.data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer<Int> idxPtr = calloc<Int>(animation.animatedMorphIndices.length);
|
// the morph targets in [animation] might be a subset of those that actually exist in the mesh (and might not have the same order)
|
||||||
|
// we don't want to reorder the data (?? or do we? this is probably more efficient for the backend?)
|
||||||
|
// so let's get the actual list of morph targets from the mesh and pass the relevant indices to the native side.
|
||||||
|
var meshMorphTargets =
|
||||||
|
await getMorphTargetNames(entity, animation.meshName);
|
||||||
|
|
||||||
|
Pointer<Int> idxPtr = calloc<Int>(animation.morphTargets.length);
|
||||||
for (int i = 0; i < animation.numMorphTargets; i++) {
|
for (int i = 0; i < animation.numMorphTargets; i++) {
|
||||||
idxPtr.elementAt(i).value = animation.animatedMorphIndices[i];
|
var index = meshMorphTargets.indexOf(animation.morphTargets[i]);
|
||||||
|
if (index == -1) {
|
||||||
|
calloc.free(dataPtr);
|
||||||
|
calloc.free(idxPtr);
|
||||||
|
throw Exception(
|
||||||
|
"Morph target ${animation.morphTargets[i]} is specified in the animation but could not be found in the mesh ${animation.meshName} under entity ${entity}");
|
||||||
|
}
|
||||||
|
idxPtr.elementAt(i).value = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lib.set_morph_animation(
|
_lib.set_morph_animation(
|
||||||
_assetManager!,
|
_assetManager!,
|
||||||
asset,
|
entity,
|
||||||
animation.meshName.toNativeUtf8().cast<Char>(),
|
animation.meshName.toNativeUtf8().cast<Char>(),
|
||||||
dataPtr,
|
dataPtr,
|
||||||
idxPtr,
|
idxPtr,
|
||||||
|
|||||||
@@ -360,19 +360,7 @@ class FilamentControllerMethodChannel extends FilamentController {
|
|||||||
///
|
///
|
||||||
Future setMorphAnimationData(
|
Future setMorphAnimationData(
|
||||||
FilamentEntity asset, MorphAnimationData animation) async {
|
FilamentEntity asset, MorphAnimationData animation) async {
|
||||||
if (_viewer == null || _resizing) {
|
throw Exception("No viewer available, ignoring");
|
||||||
throw Exception("No viewer available, ignoring");
|
|
||||||
}
|
|
||||||
await _channel.invokeMethod("setMorphAnimation", [
|
|
||||||
_assetManager,
|
|
||||||
asset,
|
|
||||||
animation.meshName,
|
|
||||||
animation.data,
|
|
||||||
animation.animatedMorphIndices,
|
|
||||||
animation.numMorphTargets,
|
|
||||||
animation.numFrames,
|
|
||||||
animation.frameLengthInMs
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user