modeling/src/maths/mat4/fromVectorRotation.js

const vec3 = require('../vec3')

const fromRotation = require('./fromRotation')

/**
 * Create a matrix that rotates the given source to the given target vector.
 *
 * Each vector must be a directional vector with a length greater than zero.
 * @see https://gist.github.com/kevinmoran/b45980723e53edeb8a5a43c49f134724
 * @param {mat4} out - receiving matrix
 * @param {vec3} source - source vector
 * @param {vec3} target - target vector
 * @returns {mat4} a new matrix
 * @alias module:modeling/maths/mat4.fromVectorRotation
 * @example
 * let matrix = fromVectorRotation(mat4.create(), [1, 2, 2], [-3, 3, 12])
 */
const fromVectorRotation = (out, source, target) => {
  const sourceNormal = vec3.normalize(vec3.create(), source)
  const targetNormal = vec3.normalize(vec3.create(), target)

  const axis = vec3.cross(vec3.create(), targetNormal, sourceNormal)
  const cosA = vec3.dot(targetNormal, sourceNormal)
  if (cosA === -1.0) return fromRotation(out, Math.PI, vec3.orthogonal(axis, sourceNormal))

  const k = 1 / (1 + cosA)
  out[0] = (axis[0] * axis[0] * k) + cosA
  out[1] = (axis[1] * axis[0] * k) - axis[2]
  out[2] = (axis[2] * axis[0] * k) + axis[1]
  out[3] = 0

  out[4] = (axis[0] * axis[1] * k) + axis[2]
  out[5] = (axis[1] * axis[1] * k) + cosA
  out[6] = (axis[2] * axis[1] * k) - axis[0]
  out[7] = 0

  out[8] = (axis[0] * axis[2] * k) - axis[1]
  out[9] = (axis[1] * axis[2] * k) + axis[0]
  out[10] = (axis[2] * axis[2] * k) + cosA
  out[11] = 0

  out[12] = 0
  out[13] = 0
  out[14] = 0
  out[15] = 1
  return out
}

module.exports = fromVectorRotation