setMorphAnimationData method

  1. @override
Future setMorphAnimationData(
  1. ThermionEntity entity,
  2. MorphAnimationData animation, {
  3. List<String>? targetMeshNames,
})
override

Implementation

@override
Future setMorphAnimationData(
    ThermionEntity entity, MorphAnimationData animation,
    {List<String>? targetMeshNames}) async {
  var meshNames = await getChildEntityNames(entity, renderableOnly: true);
  if (targetMeshNames != null) {
    for (final targetMeshName in targetMeshNames) {
      if (!meshNames.contains(targetMeshName)) {
        throw Exception(
            "Error: mesh ${targetMeshName} does not exist under the specified entity. Available meshes : ${meshNames}");
      }
    }
  }

  var meshEntities = await getChildEntities(entity, true);

  // Entities are not guaranteed to have the same morph targets (or share the same order),
  // either from each other, or from those specified in [animation].
  // We therefore set morph targets separately for each mesh.
  // For each mesh, allocate enough memory to hold FxM 32-bit floats
  // (where F is the number of Frames, and M is the number of morph targets in the mesh).
  // we call [extract] on [animation] to return frame data only for morph targets that present in both the mesh and the animation
  for (int i = 0; i < meshNames.length; i++) {
    var meshName = meshNames[i];
    var meshEntity = meshEntities[i];

    if (targetMeshNames?.contains(meshName) == false) {
      _logger.info("Skipping $meshName, not contained in target");
      continue;
    }

    var meshMorphTargets = await getMorphTargetNames(entity, meshEntity);

    var intersection = animation.morphTargets
        .toSet()
        .intersection(meshMorphTargets.toSet())
        .toList();

    if (intersection.isEmpty) {
      throw Exception(
          """No morph targets specified in animation are present on mesh $meshName.
          If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method.
          Animation morph targets: ${animation.morphTargets}\n
          Mesh morph targets ${meshMorphTargets}
          Child meshes: ${meshNames}""");
    }

    var indices =
        intersection.map((m) => meshMorphTargets.indexOf(m)).toList();

    var frameData = animation.extract(morphTargets: intersection);

    assert(frameData.length == animation.numFrames * intersection.length);

    var dataPtr = allocator<Float>(frameData.length);

    // not currently working on WASM :( wasted a lot of time figuring that out as no error is thrown
    // dataPtr
    //     .asTypedList(frameData.length)
    //     .setRange(0, frameData.length, frameData);
    for (int i = 0; i < frameData.length; i++) {
      dataPtr[i] = frameData[i];
    }

    final idxPtr = allocator<Int>(indices.length);

    for (int i = 0; i < indices.length; i++) {
      idxPtr[i] = indices[i];
    }

    var result = set_morph_animation(
        _sceneManager!,
        meshEntity,
        dataPtr,
        idxPtr,
        indices.length,
        animation.numFrames,
        animation.frameLengthInMs);
    allocator.free(dataPtr);
    allocator.free(idxPtr);
    if (!result) {
      throw Exception("Failed to set morph animation data for ${meshName}");
    }
  }
}