addBoneAnimation method

  1. @override
Future addBoneAnimation(
  1. ThermionEntity entity,
  2. BoneAnimationData animation, {
  3. int skinIndex = 0,
  4. double fadeOutInSecs = 0.0,
  5. double fadeInInSecs = 0.0,
  6. double maxDelta = 1.0,
})
override

Currently, scale is not supported.

Implementation

@override
Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation,
    {int skinIndex = 0,
    double fadeOutInSecs = 0.0,
    double fadeInInSecs = 0.0,
    double maxDelta = 1.0}) async {
  if (animation.space != Space.Bone &&
      animation.space != Space.ParentWorldRotation) {
    throw UnimplementedError("TODO - support ${animation.space}");
  }
  if (skinIndex != 0) {
    throw UnimplementedError("TODO - support skinIndex != 0 ");
  }
  var boneNames = await getBoneNames(entity);
  var restLocalTransformsRaw = allocator<Float>(boneNames.length * 16);
  get_rest_local_transforms(_sceneManager!, entity, skinIndex,
      restLocalTransformsRaw, boneNames.length);
  var restLocalTransforms = <Matrix4>[];
  for (int i = 0; i < boneNames.length; i++) {
    var values = <double>[];
    for (int j = 0; j < 16; j++) {
      values.add(restLocalTransformsRaw[(i * 16) + j]);
    }
    restLocalTransforms.add(Matrix4.fromList(values));
  }
  allocator.free(restLocalTransformsRaw);

  var numFrames = animation.frameData.length;

  var data = allocator<Float>(numFrames * 16);

  var bones = await Future.wait(List<Future<ThermionEntity>>.generate(
      boneNames.length, (i) => getBone(entity, i)));

  for (int i = 0; i < animation.bones.length; i++) {
    var boneName = animation.bones[i];
    var entityBoneIndex = boneNames.indexOf(boneName);
    if (entityBoneIndex == -1) {
      _logger.warning("Bone $boneName not found, skipping");
      continue;
    }
    var boneEntity = bones[entityBoneIndex];

    var baseTransform = restLocalTransforms[entityBoneIndex];

    var world = Matrix4.identity();
    // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble
    var parentBoneEntity = (await getParent(boneEntity))!;
    while (true) {
      if (!bones.contains(parentBoneEntity!)) {
        break;
      }
      world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world;
      parentBoneEntity = (await getParent(parentBoneEntity))!;
    }

    world = Matrix4.identity()..setRotation(world.getRotation());
    var worldInverse = Matrix4.identity()..copyInverse(world);

    for (int frameNum = 0; frameNum < numFrames; frameNum++) {
      var rotation = animation.frameData[frameNum][i].rotation;
      var translation = animation.frameData[frameNum][i].translation;
      var frameTransform =
          Matrix4.compose(translation, rotation, Vector3.all(1.0));
      var newLocalTransform = frameTransform.clone();
      if (animation.space == Space.Bone) {
        newLocalTransform = baseTransform * frameTransform;
      } else if (animation.space == Space.ParentWorldRotation) {
        newLocalTransform =
            baseTransform * (worldInverse * frameTransform * world);
      }
      for (int j = 0; j < 16; j++) {
        data.elementAt((frameNum * 16) + j).value =
            newLocalTransform.storage[j];
      }
    }

    add_bone_animation(
        _sceneManager!,
        entity,
        skinIndex,
        entityBoneIndex,
        data,
        numFrames,
        animation.frameLengthInMs,
        fadeOutInSecs,
        fadeInInSecs,
        maxDelta);
  }
  allocator.free(data);
}