function addMorphTargets(geometry, targets, parser) {

  var hasMorphPosition = false;
  var hasMorphNormal = false;

  for (var i = 0, il = targets.length; i < il; i++) {

    var target = targets[i];

    if (target.POSITION !== undefined) hasMorphPosition = true;
    if (target.NORMAL !== undefined) hasMorphNormal = true;

    if (hasMorphPosition && hasMorphNormal) break;

  }

  if (!hasMorphPosition && !hasMorphNormal) return Promise.resolve(geometry);

  var pendingPositionAccessors = [];
  var pendingNormalAccessors = [];

  for (var i = 0, il = targets.length; i < il; i++) {

    var target = targets[i];

    if (hasMorphPosition) {

      // TODO: Error-prone use of a callback inside a loop.
      var accessor = target.POSITION !== undefined ?
        parser.getDependency('accessor', target.POSITION)
        .then(function(accessor) {
          // Cloning not to pollute original accessor below
          return cloneBufferAttribute(accessor);
        }) :
        geometry.attributes.position;

      pendingPositionAccessors.push(accessor);

    }

    if (hasMorphNormal) {

      // TODO: Error-prone use of a callback inside a loop.
      var accessor = target.NORMAL !== undefined ?
        parser.getDependency('accessor', target.NORMAL)
        .then(function(accessor) {
          return cloneBufferAttribute(accessor);
        }) :
        geometry.attributes.normal;

      pendingNormalAccessors.push(accessor);

    }

  }

  return Promise.all([
    Promise.all(pendingPositionAccessors),
    Promise.all(pendingNormalAccessors)
  ]).then(function(accessors) {

    var morphPositions = accessors[0];
    var morphNormals = accessors[1];

    for (var i = 0, il = targets.length; i < il; i++) {

      var target = targets[i];
      var attributeName = 'morphTarget' + i;

      if (hasMorphPosition) {

        // Three.js morph position is absolute value. The formula is
        //   basePosition
        //     + weight0 * ( morphPosition0 - basePosition )
        //     + weight1 * ( morphPosition1 - basePosition )
        //     ...
        // while the glTF one is relative
        //   basePosition
        //     + weight0 * glTFmorphPosition0
        //     + weight1 * glTFmorphPosition1
        //     ...
        // then we need to convert from relative to absolute here.

        if (target.POSITION !== undefined) {

          var positionAttribute = morphPositions[i];
          positionAttribute.name = attributeName;

          var position = geometry.attributes.position;

          for (var j = 0, jl = positionAttribute.count; j < jl; j++) {

            positionAttribute.setXYZ(
              j,
              positionAttribute.getX(j) + position.getX(j),
              positionAttribute.getY(j) + position.getY(j),
              positionAttribute.getZ(j) + position.getZ(j)
            );

          }

        }

      }

      if (hasMorphNormal) {

        // see target.POSITION's comment

        if (target.NORMAL !== undefined) {

          var normalAttribute = morphNormals[i];
          normalAttribute.name = attributeName;

          var normal = geometry.attributes.normal;

          for (var j = 0, jl = normalAttribute.count; j < jl; j++) {

            normalAttribute.setXYZ(
              j,
              normalAttribute.getX(j) + normal.getX(j),
              normalAttribute.getY(j) + normal.getY(j),
              normalAttribute.getZ(j) + normal.getZ(j)
            );

          }

        }

      }

    }

    if (hasMorphPosition) geometry.morphAttributes.position = morphPositions;
    if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals;

    return geometry;

  });

}
Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets

@param {THREE.BufferGeometry} geometry
@param {Array<GLTF.Target>} targets
@param {GLTFParser} parser
@return {Promise<THREE.BufferGeometry>}